Klant: Useq

Project: Spatial Organ Atlas

Dataset: Kidney

date: 11 04 Thu 04 May, 2023

loading dependencies Please make sure the following packages are installed and required libraries can be loaded:

  • install.packages(“pkgbuild”) // pkgbuild::check_build_tools()
  • install.packages(“devtools”)
  • devtools::install_github(“Nanostring-Biostats/NanoStringNCTools”)
  • devtools::install_github(“Nanostring-Biostats/GeomxTools”, ref = “dev”)
  • BiocManager::install(“GeoMxWorkflows”)
  • devtools::install_github(“DavisLaboratory/standR”)
  • BiocManager::install(“SpatialDecon”)
  • BiocManager::install(“GSVA”)
  • install.packages(“plotly”)
  • install.packages(“DT”)
  • install.packages(“msigdbr”)
  • install.packages(“digest”)
  • install.packages(“rmarkdown”)
  • install.packages(“kable”)
  • BiocManager::install(“EBImage”)
  • install.packages(‘scattermore’)
  • install.packages(‘pbapply’)
  • install.packages(‘plotrix’)
  • install.packages(‘ggtext’)
  • install.packages(‘RBioFormats’)
  • BiocManager::install(“aoles/RBioFormats”)
  • install.packages(“C:/Users/inijman/Downloads/SpatialOmicsOverlay-0.99.13-beta.tar.gz”, dependencies = TRUE, repos = NULL)
  • devtools::install_github(“DavisLaboratory/standR”)
  • BiocManager::install(“clusterProfiler”)
  • BiocManager::install(“pathview”)
#load libraries

#stack_size <- getOption("pandoc.stack.size", default = "512m")
#options(java.parameters = c("-XX:+UseConcMarkSweepGC", "-Xmx8192m, -XX:MetaspaceSize=1024M"))
options(java.parameters = c("-XX:+UseConcMarkSweepGC", "-Xmx8192m"))
library(NanoStringNCTools)
library(GeomxTools)
library(GeoMxWorkflows)
library(SpatialDecon)
library(GSVA) #for pathway analyses
library(msigdbr) #for molecular signatures in pathway analyses
library(knitr)
library(dplyr)
library(ggforce)
library(ggplot2)
library(scales) # for percent
library(reshape2)  # for melt
library(cowplot)   # for plot_grid
library(umap)
library(Rtsne)
library(pheatmap)  # for pheatmap
library(ggrepel) 
library(scales) #for ggplot peaudolog to prevent errors on log(0)
library(DT)
library(plotly)
library(gridExtra)
library(RColorBrewer)
library(SpatialOmicsOverlay)
library(gt)
library(tidyverse)
library(clusterProfiler)
library(fgsea)
library(pathview)
library(png)
library(readxl)
library(viridis)

library(standR)
library(SpatialExperiment)
library(limma)
library(ggalluvial)
library(scater)

#BiocManager::install("sva")
library(sva)

#BiocManager::install("preprocessCore") #quantile norm
library(preprocessCore)

#install.packages("Polychrome") #Get colors
#library(Polychrome)


library(infercnv) # CNV
library("ape")
library("Biostrings")
library("ggtree")
library(tidytree)
library(ggpubr)

1 loading base files

# Reference the main folder 'file.path' containing the sub-folders with each data file type:
datadir<-file.path("L:/pkloosterman/Github/DKD_Kidney/")
#datadir<-file.path("E:/DKD_Kidnet/")

To locate a specific file path replace the above line with datadir <- file.path(“~/Folder/SubFolder/DataLocation”) replace the Folder, SubFolder, DataLocation as needed. The DataLocation folder should contain a dccs, pkcs, and annotation folder with each set of files present as needed automatically list files in each directory for use.

Take care you import a column with nuclei count separately if you want.

DCCFiles <- dir(file.path(datadir, "dccs"), pattern = ".dcc$",
                full.names = TRUE, recursive = TRUE)
PKCFiles <- dir(file.path(datadir, "pkcs"), pattern = ".pkc$",
                full.names = TRUE, recursive = TRUE)
SampleAnnotationFile <-
  dir(file.path(datadir, "annotation"), pattern = "^[^~]",
      full.names = TRUE, recursive = TRUE)

2 load data

Data <-
  readNanoStringGeoMxSet(dccFiles = DCCFiles,
                         pkcFiles = PKCFiles,
                         phenoDataFile = SampleAnnotationFile,
                         phenoDataSheet = "Template",
                         phenoDataDccColName = "Sample_ID",
                         protocolDataColNames = c("aoi", "roi"),
                         experimentDataColNames = c("panel"))

#save data to prevent loading time for retakes
#saveData<-Data
#Data<-saveData

#change Data column names and manual correction of spelling errors
Data@phenoData@data[["slide_name"]]<-Data@phenoData@data[["slide name"]]
Data@phenoData@data[["slide name"]]<-  NULL


#+1 references the slide name column
ann_size<-length(colnames(Data@phenoData@data)[grepl("ANN",colnames(Data@phenoData@data))])+1 
ann_names<-c(colnames(Data@phenoData@data)[grepl("ANN",colnames(Data@phenoData@data))],"slide_name")

# Feel free to change the order of which colors are appointed.
color<-c("#A349A4", "#FFFF33", "#E7298A", "#091833", "#1B9E77", "#D95F02", "#7570B3",  "#66A61E", "#E6AB02", "#8DD3C7", "#9F000F", "#BEBADA", "#FB8072", "#80B1D3", "#FDB462", "#B3DE69", "#FCCDE5", "#D9D9D9", "#BC80BD", "#CCEBC5", "#FFED6F", "#377EB8", "#984EA3", "#4DAF4A", "#FF71CE", "#FF7F00", "#A6CEE3", "#1F78B4", "#B2DF8A", "#33A02C", "#FB9A99", "#E31A1C", "#FDBF6F", "#CAB2D6", "#6A3D9A", "#FFFF99", "#B15928")

# Use count and count_max to set the range of color needed for each column.
# With setNames() the color code is linked to each unique individual value of each column.
count=1
color_list = list()
for (ann in ann_names) {
  count_max = count+length(unique(Data@phenoData@data[[ann]]))-1
  color_list[[ann]]<-setNames(color[count:count_max], unique(Data@phenoData@data[[ann]]))
  count=count_max+1
}
value_names = c()
for (ann in ann_names) {
  value_names <- c(value_names, unique(Data@phenoData@data[[ann]]))
}

color_df <- as.data.frame(value_names)
color_df$color <- color[0:length(value_names)]

color_df %>% 
  mutate(color = fct_inorder(color)) |> 
  gt() %>% 
  data_color(columns = color, colors = as.character(color)) %>%
  tab_options(container.height = 500)
value_names color
DKD #A349A4
normal #FFFF33
disease3 #E7298A
disease4 #091833
normal3 #1B9E77
normal4 #D95F02
disease1B #7570B3
disease2B #66A61E
normal2B #E6AB02
glomerulus #8DD3C7
tubule #9F000F
abnormal #BEBADA
NA #FB8072
normal #80B1D3
niormal #FDB462
disease3 #B3DE69
disease4 #FCCDE5
normal3 #D9D9D9
normal4 #BC80BD
disease1B #CCEBC5
disease2B #FFED6F
normal2B #377EB8
paste("Reads from following runs used: ",unique(pData(protocolData(Data))$SeqSetId))
## [1] "Reads from following runs used:  "

3 Study design

pkcs <- annotation(Data)
modules <- gsub(".pkc", "", pkcs)
kable(data.frame(PKCs = pkcs, modules = modules))
PKCs modules
TAP_H_WTA_v1.0.pkc TAP_H_WTA_v1.0

Select the annotations we want to show, use `` to surround column names with spaces or special symbols

#count_mat <- dplyr::count(pData(Data), ANN1,ANN2,slide_name)
count_mat <- pData(Data) %>% dplyr::count(pData(Data)[c(ann_names)])

Simplify the slide names if required

# count_mat$slide_name <- gsub("disease", "d", gsub("normal", "n", count_mat$slide_name))
#count_mat$path_ann <- gsub("i", "", count_mat$path_ann) #correcting spelling error

Gather the data and plot in order: class, slide name, region, segment

test_gr <- gather_set_data(count_mat, 1:all_of(ann_size))
test_gr$x <- factor(test_gr$x, labels = ann_names)

Plot Sankey

ggplot(test_gr, height = 10, width = 10, aes(x, id = id, split = y, value = n)) +
  geom_parallel_sets(aes(fill = ANN2), alpha = 0.5, axis.width = 0.1) +
  geom_parallel_sets_axes(axis.width = 0.2) +
  geom_parallel_sets_labels(color = "white", size = 5) +
  theme_classic(base_size = 12) + 
  theme(legend.position = "bottom",
        axis.ticks.y = element_blank(),
        axis.line = element_blank(),
        axis.text.y = element_blank()) +
  scale_y_continuous(expand = expansion(0)) + 
  scale_x_discrete(expand = expansion(0)) +
  labs(x = "", y = "") +
  scale_fill_manual(values=color_list$ANN2) +
  annotate(geom = "segment", x = 4.25, xend = 4.25,
           y = 10, yend = 61, lwd = 2) +
  annotate(geom = "text", x = 4.19, y = 25, angle = 90, size = 5,
           hjust = 0.5, label = "50 segments")

4 QC & Pre-processing

Shift counts to one

#shift any expression counts with a value of 0 to 1 to enable in downstream transformations.
Data <- shiftCountsOne(Data, useDALogic = TRUE)

4.1 Segment QC

We first assess sequencing quality and adequate tissue sampling for every ROI/AOI segment.

Every ROI/AOI segment will be tested for:

Raw sequencing reads: segments with >1000 raw reads are removed. % Aligned,% Trimmed, or % Stitched sequencing reads: segments below ~80% for one or more of these QC parameters are removed. % Sequencing saturation ([1-deduplicated reads/aligned reads]%): segments below ~50% require additional sequencing to capture full sample diversity and are not typically analyzed until improved. Negative Count: this is the geometric mean of the several unique negative probes in the GeoMx panel that do not target mRNA and establish the background count level per segment; segments with low negative counts (1-10) are not necessarily removed but may be studied closer for low endogenous gene signal and/or insufficient tissue sampling. No Template Control (NTC) count: values >1,000 could indicate contamination for the segments associated with this NTC; however, in cases where the NTC count is between 1,000- 10,000, the segments may be used if the NTC data is uniformly low (e.g. 0-2 counts for all probes). Nuclei: >100 nuclei per segment is generally recommended; however, this cutoff is highly study/tissue dependent and may need to be reduced; what is most important is consistency in the nuclei distribution for segments within the study. Area: generally correlates with nuclei; a strict cutoff is not generally applied based on area.

4.1.1 Select Segment QC

First, we select the QC parameter cutoffs, against which our ROI/AOI segments will be tested and flagged appropriately. We have selected the appropriate study-specific parameters for this study. Note: the default QC values recommended above are advised when surveying a new dataset for the first time.

Default QC cutoffs are commented in () adjacent to the respective parameters study-specific values were selected after visualizing the QC results in more detail below

QC_params <-
  list(minSegmentReads = 1000, # Minimum number of reads (1000)
       percentTrimmed = 80,    # Minimum % of reads trimmed (80%)
       percentStitched = 80,   # Minimum % of reads stitched (80%)
       percentAligned = 75,    # Minimum % of reads aligned (80%)
       percentSaturation = 50, # Minimum sequencing saturation (50%)
       minNegativeCount = 1,   # Minimum negative control counts (10)
       maxNTCCount = 9000,     # Maximum counts observed in NTC well (1000)
       minNuclei = 20,        # Minimum # of nuclei estimated (100)
       minArea = 1000)         # Minimum segment area (5000)
Data <-
  setSegmentQCFlags(Data, qcCutoffs = QC_params)        

cat("pre-QC features:", dim(Data)[1], "\npre-QC samples:", dim(Data)[2])
## pre-QC features: 18642 
## pre-QC samples: 276
#Table for clarification
QCparams_df <- data.frame (
  items = c("minSegmentReads","percentTrimmed","percentStitched","percentAligned","percentSaturation",
            "minNegativeCount","maxNTCCount","minNuclei","minArea"),
  defaults = c(1000,80,80,80,50,10,1000,100,5000),
  actual = c(QC_params$minSegmentReads,QC_params$percentTrimmed,QC_params$percentStitched,QC_params$percentAligned,QC_params$percentSaturation,QC_params$minNegativeCount,QC_params$maxNTCCount,QC_params$minNuclei,QC_params$minArea)
)

datatable(QCparams_df, rownames=FALSE,
          caption = "QC thresholds",
          extensions = 'Buttons', options = list (
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          )
)

Collate QC Results

QCResults <- protocolData(Data)[["QCFlags"]]
flag_columns <- colnames(QCResults)
QC_Summary <- data.frame(Pass = colSums(!QCResults[, flag_columns]),
                         Warning = colSums(QCResults[, flag_columns]))

QCResults$QCStatus <- apply(QCResults, 1L, function(x) {
  ifelse(sum(x) == 0L, "PASS", "WARNING")
})

QC_Summary["TOTAL FLAGS", ] <-
  c(sum(QCResults[, "QCStatus"] == "PASS"),
    sum(QCResults[, "QCStatus"] == "WARNING"))

col_by <- "ANN1"
col_by_plate <- "Plate_ID"

4.2 Graphical summaries of QC statistics

Use the tab-menu to navigate!

QC_histogram <- function(assay_data = NULL,
                         annotation = NULL,
                         fill_by = NULL,
                         thr = NULL,
                         scale_trans = NULL) {
  plt <- ggplot(assay_data,
                aes_string(x = paste0("unlist(`", annotation, "`)"),
                           fill = fill_by)) +
    geom_histogram(bins = 50) +
    geom_vline(xintercept = thr, lty = "dashed", color = "black") +
    theme_bw() + guides(fill = "none") +
    facet_wrap(as.formula(paste("~", fill_by)), nrow = 4) +
    scale_fill_manual(values=color_list$ANN1) +
    labs(x = annotation, y = "segments, #", title = annotation)
  if(!is.null(scale_trans)) {
    plt <- plt +
      scale_x_continuous(trans = scale_trans)
  }
  plt
}

Trimmed

% Trimmed sequencing reads: segments below the QC parameters are removed.

QC_histogram(sData(Data), "Trimmed (%)", col_by, QC_params$percentTrimmed)

Stiched (%)

% Stiched sequencing reads: segments below the QC parameters are removed.

QC_histogram(sData(Data), "Stitched (%)", col_by, QC_params$percentStitched)

Aligned (%)

% Aligned sequencing reads: segments below the QC parameters are removed.

QC_histogram(sData(Data), "Aligned (%)", col_by,QC_params$percentAligned)

Sequencing Saturation (%)

% Sequencing saturation ([1-deduplicated reads/aligned reads]%): segments below the QC parameter require additional sequencing to capture full sample diversity and are not typically analyzed until improved.

QC_histogram(sData(Data), "Saturated (%)", col_by, QC_params$percentSaturation) +
  labs(title = "Sequencing Saturation (%)",
       x = "Sequencing Saturation (%)")

Area

Area: generally correlates with nuclei; a strict cutoff is not generally applied based on area.

QC_histogram(sData(Data), "area", col_by, QC_params$minArea, scale_trans = "log10")

Nuclei count

Nuclei: >100 nuclei per segment is generally recommended; however, this cutoff is highly study/tissue dependent and may need to be reduced; what is most important is consistency in the nuclei distribution for segments within the study.

QC_histogram(sData(Data), "nuclei", col_by, QC_params$minNuclei)

DuplicationRate

Show the percentage of duplicated reads per raw reads.

ggplot(pData(protocolData(Data)),
       aes(x = Plate_ID, fill=Plate_ID,
          y = (DeduplicatedReads/Raw))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Deduplicated / Raw reads") +
  scale_y_continuous(labels = scales::percent) +
  theme_bw()

Negprobes vs Endogenous

Compare Negprobes vs Endogenous expression to see if the endogenous (real) probes are significantly higher than Negprobes. This means that there is a high signal-to-noise ratio.

tmp_target_Data <- aggregateCounts(Data)

#get negative probe data
negs<-subset(tmp_target_Data,CodeClass=="Negative")

p1<-ggplot(pData(negs),
       aes(x = ANN2, fill = ANN2,
          y = assayDataElement(negs, elt = "exprs"))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Negative probes Expression") +
  scale_y_continuous(limits = c(1,3000), trans = "log2") +
  theme_bw() + coord_flip()


# get endogenous probe data
end<-subset(tmp_target_Data,CodeClass=="Endogenous")

p2<-ggplot(pData(end),
       aes(x = ANN2, fill = ANN2,
           y = colMeans(assayDataElement(end, elt = "exprs")))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Endogenous probes Expression (mean)") +
  scale_y_continuous(limits = c(1,3000),trans = "log2") +
  theme_bw() + coord_flip()

pl <-list(p1,p2)
plot_grid(plotlist=pl, nrow=2, align='v')

Neg_probe reads compared to raw_reads

Show the difference between deduplicated reads and negative control reads. It is expected that deduplicated reads are substantially higher than negative control reads.

# make background total neg probe count
fdata_df<-fData(Data)
negprobesnames<-rownames(fdata_df[fdata_df$Negative==TRUE,])
temp_exp<-assayDataElement(Data,elt='exprs')
negprobe_expr_fd<-temp_exp[rownames(temp_exp) %in% negprobesnames,]
tot_neg_ctrl_reads<-colSums(negprobe_expr_fd)
tot_dedup_reads<-pData(protocolData(Data))$DeduplicatedReads

df<-data.frame('aoi'= names(tot_neg_ctrl_reads),'tot_dedup_reads' = as.numeric(tot_dedup_reads),'tot_neg_ctrl_reads'=as.numeric(tot_neg_ctrl_reads))
df<-melt(df,id="aoi")
ggplot(df,aes(fill=variable,y=value,x=aoi)) + 
  geom_bar(position="identity",stat="identity") +
  scale_y_continuous(trans = log2_trans()) +
  theme(legend.position="bottom",axis.text.x = element_blank(),axis.ticks.x=element_blank())                     

Duplicated reads vs Background

Compare duplicated reads and background noise to show that the reads significantly duplicated and thus checked to avoid the questioning about the validity of the reads.

# get dcc per plate. sum negprobe counts/dcc/plate
ggplot(pData(protocolData(Data)),
       aes(x = Plate_ID, fill=Plate_ID,
          y = DeduplicatedReads)) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = "Deduplicated / Raw reads") +
  scale_y_log10()+
  geom_hline(data =pData(protocolData(Data)) , 
           aes(yintercept = NTC, colour=Plate_ID)) +
  theme_bw()

Duplicated reads vs ROIarea

See if all ROIs contain duplicated reads for validity.

temp_df<-cbind(pData(Data),pData(protocolData(Data)),dcc=rownames(pData(Data)))

ggplot(temp_df,
       aes(x = dcc, colour=slide_name,
          y = (DeduplicatedReads/area) )) +
  geom_point() +
  ylim(0,20) + 
  labs(y = "Deduplicated reads / ROI area") +
  theme(axis.text.x = element_text(size =6, angle=90, hjust=1) )

Duplicated reads vs nuclei

Show the amount of deduplicated reads per nuclei.

temp_df<-cbind(pData(Data),pData(protocolData(Data)),dcc=rownames(pData(Data)))

ggplot(temp_df,
       aes(x = dcc, colour=slide_name,
          y = (DeduplicatedReads/nuclei) )) +
  geom_point() +
  ylim(0,50) +                                                      # Adjust per project
  labs(y = "Deduplicated reads / nuclei") +
  theme(axis.text.x = element_text(size =6, angle=90, hjust=1) )

4.3 Process Negative GeoMeans

Negative Count: this is the geometric mean of the several unique negative probes in the GeoMx panel that do not target mRNA and establish the background count level per segment; segments with low negative counts (1-10) are not necessarily removed but may be studied closer for low endogenous gene signal and/or insufficient tissue sampling.

# Calculate the negative geometric means for each module
# It will show only the negative probes geomean, so expect less segments.
negativeGeoMeans <- 
  esBy(negativeControlSubset(Data), 
       GROUP = "Module", 
       FUN = function(x) { 
         assayDataApply(x, MARGIN = 2, FUN = ngeoMean, elt = "exprs") 
       }) 
protocolData(Data)[["NegGeoMean"]] <- negativeGeoMeans

negCols <- paste0("NegGeoMean_", modules)
pData(Data)[, negCols] <- sData(Data)[["NegGeoMean"]]
for(ann in negCols) {
  plt <- QC_histogram(pData(Data), ann, col_by, 2, scale_trans = "log10")
  print(plt)
}

# Detatch neg_geomean columns ahead of aggregateCounts call

pData(Data) <- pData(Data)[, !colnames(pData(Data)) %in% negCols]

Show all NTC values, Freq = # of Segments with a given NTC count:

QC<-sData(Data)

ntc_df <-QC[,c("slide_name","Plate_ID","NTC_ID","NTC")]
temptable<-ntc_df %>% dplyr::count(ntc_df$slide_name,ntc_df$NTC_ID,ntc_df$Plate_ID,ntc_df$NTC)
colnames(temptable) <- c("Slide_name","NTC_ID","Plate_ID","NTC_count","Number_of_samples")
datatable(temptable, rownames = FALSE)
kable(table(NTC_Count = sData(Data)$NTC), col.names = c("NTC Count", "# of Segments"))
NTC Count # of Segments
3 64
113 71
397 47
8704 94
kable(QC_Summary, caption = "QC Summary Table for each Segment")
QC Summary Table for each Segment
Pass Warning
LowReads 272 4
LowTrimmed 276 0
LowStitched 273 3
LowAligned 266 10
LowSaturation 272 4
LowNegatives 276 0
HighNTC 276 0
LowNuclei 276 0
LowArea 265 11
TOTAL FLAGS 259 17
datatable(QC_Summary,
          caption = "AOI QC Summary",
          extensions = 'Buttons', options = list (
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          )
)

Show AOIs which fail critical QCs.

QC<-sData(Data)
undersat<-subset(QC, `Saturated (%)`<= QC_params$percentSaturation)

if(nrow(undersat)> 0) {

datatable(aggregate(undersat, by=list(undersat$SampleID),paste,collapse=";"),
          extensions = 'Buttons', options = list (
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          )
)}

Show the reasons why the AOIs fail critical QCs.

if(nrow(undersat)> 0) {

datatable(aggregate(undersat$QCFlags, by=list(undersat$SampleID),paste,collapse=";"),
          extensions = 'Buttons', options = list (
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          )) %>%
    formatStyle(columns = colnames(undersat$QCFlags), backgroundColor = styleEqual("TRUE", "#850000"), color = styleEqual("TRUE", "white")) 
  }

Subsetting our dataset has removed samples which did not pass QC

Data <- Data[, QCResults$QCStatus == "PASS"]

Generally keep the qcCutoffs parameters unchanged. Set removeLocalOutliers to FALSE if you do not want to remove local outliers

Data <- setBioProbeQCFlags(Data, 
                               qcCutoffs = list(minProbeRatio = 0.1,
                                                percentFailGrubbs = 20), 
                               removeLocalOutliers = FALSE)

ProbeQCResults <- fData(Data)[["QCFlags"]]

Define QC table for Probe QC

qc_df <- data.frame(Passed = sum(rowSums(ProbeQCResults[, -1]) == 0),
                    Global = sum(ProbeQCResults$GlobalGrubbsOutlier),
                    Local = sum(rowSums(ProbeQCResults[, -2:-1]) > 0
                                & !ProbeQCResults$GlobalGrubbsOutlier))

Subset object to exclude all that did not pass Ratio & Global testing

ProbeQCPassed <- 
  subset(Data, 
         fData(Data)[["QCFlags"]][,c("LowProbeRatio")] == FALSE &
           fData(Data)[["QCFlags"]][,c("GlobalGrubbsOutlier")] == FALSE)

Data <- ProbeQCPassed 
cat("After QC features:", dim(Data)[1], "\nAfter QC samples:", dim(Data)[2])
## After QC features: 18641 
## After QC samples: 259

Check how many unique targets the object has

length(unique(featureData(Data)[["TargetName"]]))
## [1] 18504

Collapse to targets

target_Data <- aggregateCounts(Data)

exprs(target_Data)[1:5, 1:2]
##       DSP-1001250007851-H-A02.dcc DSP-1001250007851-H-A03.dcc
## A2M                           485                         262
## NAT2                           15                          18
## ACADM                          31                          15
## ACADS                          27                          17
## ACAT1                          29                          24

Define LOQ SD threshold and minimum value

cutoff <- 2
minLOQ <- 2

4.4 Limit of Quantification

We define a limit of quantification (LOQ) per ROI/AOI segment based on the negative control probes to guide the filtering of segments and genes with low signal relative to background. The formula for calculating the LOQ in the \(i^{th}\) segment at \(n\) standard deviations (\(n = 2\) for this study) is: \(LOQ_i=geomean(NegProbe_i)*geoSD(NegProbe_i)^n\)

Calculate LOQ per module tested

LOQ <- data.frame(row.names = colnames(target_Data))
for(module in modules) {
  vars <- paste0(c("NegGeoMean_", "NegGeoSD_"),
                 module)
  if(all(vars[1:2] %in% colnames(pData(target_Data)))) {
    LOQ[, module] <-
      pmax(minLOQ,
           pData(target_Data)[, vars[1]] * 
             pData(target_Data)[, vars[2]] ^ cutoff)
  }
}
pData(target_Data)$LOQ <- LOQ

4.5 Filtering

After determining the limit of quantification (LOQ) per segment, we recommend filtering out either segments and/or genes with abnormally low signal. Filtering is an important step to focus on the true biological data of interest.

We determine the number of genes detected in each segment across the dataset.

LOQ_Mat <- c()
for(module in modules) {
  ind <- fData(target_Data)$Module == module
  Mat_i <- t(esApply(target_Data[ind, ], MARGIN = 1,
                     FUN = function(x) {
                       x > LOQ[, module]
                     }))
  LOQ_Mat <- rbind(LOQ_Mat, Mat_i)
}
# ensure ordering since this is stored outside of the geomxSet
LOQ_Mat <- LOQ_Mat[fData(target_Data)$TargetName, ]

4.5.1 Segment Gene Detection

We first filter out segments with exceptionally low signal. These segments will have a small fraction of panel genes detected above the LOQ relative to the other segments in the study. Let’s visualize the distribution of segments with respect to their % genes detected:

Save detection rate information to pheno data

pData(target_Data)$GenesDetected <- 
  colSums(LOQ_Mat, na.rm = TRUE)
pData(target_Data)$GeneDetectionRate <-
  pData(target_Data)$GenesDetected / nrow(target_Data)

Determine detection thresholds: 1%, 5%, 10%, 15%, >15%

pData(target_Data)$DetectionThreshold <- 
  cut(pData(target_Data)$GeneDetectionRate,
      breaks = c(0, 0.01, 0.05, 0.1, 0.15, 0.2,1),
      labels = c("<1%", "1-5%", "5-10%", "10-15%", "15-20%", ">20%"))

# stacked bar plot of different cut points (1%, 5%, 10%, 15%)
ggplot(pData(target_Data),
       aes(x = DetectionThreshold)) +
  geom_bar(aes(fill = ANN2)) +
  geom_text(stat = "count", aes(label = ..count..), vjust = -0.5) +
  theme_bw() +
  scale_y_continuous(expand = expansion(mult = c(0, 0.1))) +
  scale_fill_manual(values=color_list$ANN2) +
  labs(x = "Gene Detection Rate",
       y = "Segments, #",
       fill = "Segment Type")

cut percent genes detected at 1, 5, 10, 15

kable(table(pData(target_Data)$DetectionThreshold,
            pData(target_Data)$ANN2))
disease1B disease2B disease3 disease4 normal2B normal3 normal4
<1% 0 0 0 0 1 0 0
1-5% 0 1 0 0 6 0 0
5-10% 8 3 0 1 5 1 0
10-15% 13 11 1 3 2 3 0
15-20% 7 5 3 7 5 10 0
>20% 5 11 55 13 15 41 23
# set threshold for detectionlevel
# default 0.1
gene_det_threshold <- 0.05

target_Data <-
  target_Data[, pData(target_Data)$GeneDetectionRate >= gene_det_threshold]

dim(target_Data)
## Features  Samples 
##    18504      251

4.6 Manual removal of samples/classes

active_aois<-names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))

re-Collect annotations

# gather the data and plot in order: class, slide name, region, segment
#count_mat <- dplyr::count(pData(Data), ANN1,ANN2,ANN3,ANN4,slide_name)

temp_qc <- temp_df
temp_qc$QCResult <- QCResults$QCStatus
temp_qc$QCResult[temp_qc$QCResult == "WARNING"] <- "X"

#count_mat <- dplyr::count(a, ANN1, ANN2, slide_name, QCResult)
count_mat <- temp_qc %>% dplyr::count(temp_qc[c(ann_names, "QCResult")])

test_gr <- gather_set_data(count_mat, 1:(ann_size+1))
test_gr$x <- factor(test_gr$x, labels = c(ann_names, "QCResult"))

re-Plot Sankey

ggplot(test_gr, aes(x, id = id, split = y, value = n)) +
  geom_parallel_sets(aes(fill = ANN2), alpha = 0.5, axis.width = 0.1) +
  geom_parallel_sets_axes(axis.width = 0.2) +
  geom_parallel_sets_labels(color = "white", size = 5) +
  theme_classic(base_size = 17) + 
  theme(legend.position = "bottom",
        axis.ticks.y = element_blank(),
        axis.line = element_blank(),
        axis.text.y = element_blank()) +
  scale_y_continuous(expand = expansion(0)) + 
  scale_x_discrete(expand = expansion(0)) +
  scale_fill_manual(values=color_list$ANN2) +
  labs(x = "", y = "") +
  annotate(geom = "segment", x = 3.25, xend = 3.25, y = 10, 
           yend = 60, lwd = 2) +
  annotate(geom = "text", x = 3.19, y = 25, angle = 90, size = 5,
           hjust = 0.5, label = "50 segments")

4.7 Gene Detection Rate

Calculate detection rate

LOQ_Mat <- LOQ_Mat[, colnames(target_Data)]
fData(target_Data)$DetectedSegments <- rowSums(LOQ_Mat, na.rm = TRUE)
fData(target_Data)$DetectionRate <-
  fData(target_Data)$DetectedSegments / nrow(pData(target_Data))

Gene of interest detection table

goi <- c("PDCD1", "CD274", "IFNG", "CD8A", "CD68", "EPCAM",
         "KRT18", "NPHS1", "NPHS2", "CALB1", "CLDN8")
goi_df <- data.frame(
  Gene = goi,
  Number = fData(target_Data)[goi, "DetectedSegments"],
  DetectionRate = percent(fData(target_Data)[goi, "DetectionRate"]))

4.8 Gene Filtering

We will graph the total number of genes detected in different percentages of segments. Based on the visualization below, we can better understand global gene detection in our study and select how many low detected genes to filter out of the dataset. Gene filtering increases performance of downstream statistical tests and improves interpretation of true biological signal.

Plot detection rate

plot_detect <- data.frame(Freq = c(1, 5, 10, 20, 30, 50))

# Manual gene cut-off
cutoff <- 5
cutoff <- which(sapply(plot_detect$Freq, FUN=function(X) cutoff %in% X)) + 0.5 # Select step from plot_detect. 1 = 1, 5 = 2, 10 = 3, etc. +0.5 to dodge bin.

plot_detect$Number <-
  unlist(lapply(c(0.01, 0.05, 0.1, 0.2, 0.3, 0.5),
                function(x) {sum(fData(target_Data)$DetectionRate >= x)}))
plot_detect$Rate <- plot_detect$Number / nrow(fData(target_Data))
rownames(plot_detect) <- plot_detect$Freq

ggplot(plot_detect, aes(x = as.factor(Freq), y = Rate, fill = Rate)) +
  geom_bar(stat = "identity") +
  geom_text(aes(label = formatC(Number, format = "d", big.mark = ",")),
            vjust = 1.6, color = "black", size = 4) +
  
  geom_vline(xintercept = 3.5, color = "red") +
  geom_vline(xintercept = cutoff, color = "red") +
  annotate(x=3.5,y=0.1,label="Default cut-off",size=3,geom="label") +
  annotate(x=cutoff,y=0.1,label="Manual cut-off",size=3,geom="label") +
  
  scale_fill_gradient2(low = "orange2", mid = "lightblue",
                       high = "dodgerblue3", midpoint = 0.65,
                       limits = c(0,1),
                       labels = scales::percent) +
  theme_bw() +
  scale_y_continuous(labels = scales::percent, limits = c(0,1),
                     expand = expansion(mult = c(0, 0))) +
  labs(x = "% of Segments",
       y = "Genes Detected, % of Panel > LOQ")

Subset to target genes detected in at least a manual percentage of the samples with a default of 10%. Also manually include the negative control probe, for downstream use.

# default=0.1
negativeProbefData <- subset(fData(target_Data), CodeClass == "Negative")
neg_probes <- unique(negativeProbefData$TargetName)
target_Data <- 
  target_Data[fData(target_Data)$DetectionRate >= 0.05 |
                    fData(target_Data)$TargetName %in% neg_probes, ]

# retain only detected genes of interest
goi <- goi[goi %in% rownames(target_Data)]

dim(target_Data)
## Features  Samples 
##    12241      251

5 Normalization

We will now normalize the GeoMx data for downstream visualizations and differential expression. The two common methods for normalization of DSP-NGS RNA data are i) quartile 3 (Q3) or ii) background normalization.

Both of these normalization methods estimate a normalization factor per segment to bring the segment data distributions together. More advanced methods for normalization and modeling are under active development. However, for most studies, these methods are sufficient for understanding differences between biological classes of segments and samples.

Q3 normalization is typically the preferred normalization strategy for most DSP-NGS RNA studies. Given the low negative probe counts in this particular dataset as shown during Segment QC, we would further avoid background normalization as it may be less stable.

Before normalization, we will explore the relationship between the upper quartile (Q3) of the counts in each segment with the geometric mean of the negative control probes in the data. Ideally, there should be a separation between these two values to ensure we have stable measure of Q3 signal. If you do not see sufficient separation between these values, you may consider more aggressive filtering of low signal segments/genes.

Q3 and quantile normalization are radically different in their approach. Q3 normalization relies a normalization factor that aligns the third quartile gene count value for all samples. This method does not address potential global variations in data distributions or expression ranges. In contrast, quantile normalization, which is widely used to normalize gene expression data derived from microarrays, aligns count values according to the rank of the genes and forces the data of all samples into the same distribution. It is therefore inherent to this method that the difference in count value range or signal-to-noise ratio is no longer dependent on raw data distributions1.

note that quantile normalization, due to its course method of normalization, may limit the detection of more subtle differences in gene expression.

Graph Q3 value vs negGeoMean of Negatives

ann_of_interest <- "ANN2"
Stat_data <- 
  data.frame(row.names = colnames(exprs(target_Data)),
             Segment = colnames(exprs(target_Data)),
             Annotation = pData(target_Data)[, ann_of_interest],
             Q3 = unlist(apply(exprs(target_Data), 2,
                               quantile, 0.75, na.rm = TRUE)),
             NegProbe = exprs(target_Data)[neg_probes, ])
Stat_data_m <- melt(Stat_data, measure.vars = c("Q3", "NegProbe"),
                    variable.name = "Statistic", value.name = "Value")

plt1 <- ggplot(Stat_data_m,
               aes(x = Value, fill = Statistic)) +
  geom_histogram(bins = 40) + theme_bw() +
  scale_x_continuous(trans = "log2") +
  facet_wrap(~Annotation, nrow = 1) + 
  scale_fill_brewer(palette = 3, type = "qual") +
  labs(x = "Counts", y = "Segments, #")

plt2 <- ggplot(Stat_data,
               aes(x = NegProbe, y = Q3, color = Annotation)) +
  geom_abline(intercept = 0, slope = 1, lty = "dashed", color = "darkgray") +
  geom_point() + guides(color = "none") + theme_bw() +
  scale_x_continuous(trans = "log2") + 
  scale_y_continuous(trans = "log2") +
  theme(aspect.ratio = 1) +
  labs(x = "Negative Probe GeoMean, Counts", y = "Q3 Value, Counts")

plt3 <- ggplot(Stat_data,
               aes(x = NegProbe, y = Q3 / NegProbe, color = Annotation)) +
  geom_hline(yintercept = 1, lty = "dashed", color = "darkgray") +
  geom_point() + theme_bw() +
  scale_x_continuous(trans = "log2") + 
  scale_y_continuous(trans = "log2") +
  theme(aspect.ratio = 1) +
  labs(x = "Negative Probe GeoMean, Counts", y = "Q3/NegProbe Value, Counts")

btm_row <- plot_grid(plt2, plt3, nrow = 1, labels = c("B", ""),
                     rel_widths = c(0.43,0.57))
plot_grid(plt1, btm_row, ncol = 1, labels = c("A", ""))

Q3 norm (75th percentile) for WTA/CTA with or without custom spike-ins

target_Data <- normalize(target_Data ,
                             norm_method = "quant", 
                             desiredQuantile = .75,
                             toElt = "q_norm")
#, data_type = "RNA" depricated after 4.1

Quantile Normalization

quantile <- normalize.quantiles(target_Data@assayData[["exprs"]])
rownames(quantile) <- rownames(target_Data@assayData[["exprs"]])
colnames(quantile) <- colnames(target_Data@assayData[["exprs"]])

assayDataElement(object = target_Data, elt = "quantile_norm") <-  quantile

Background normalization for WTA/CTA without custom spike-in

target_Data <- normalize(target_Data,
                             norm_method = "neg", 
                             fromElt = "exprs",
                             toElt = "neg_norm")

# , data_type = "RNA" depricated after 4.1

Overwrite the dafault q_norm with another normalization method if needed

# Normalization options are: default "q_norm", "neg_norm" or "quantile_norm".
assayDataElement(object = target_Data, elt = "q_norm") <- assayDataElement(object = target_Data, elt = "q_norm") 

5.1 Visualize the first 10 segments with each normalization method

Use the tab-menu to navigate!

#Fix zero values, which go to -inf in log transform in standard boxplot
# temp <-as.matrix(exprs((target_Data)[,1:10]))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
#     geom_boxplot(fill="#9EDAE5") +
#     scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
#     scale_x_discrete(labels=c(1:10)) +
#     labs(title="Raw counts", x="segment", y = "Counts, Raw")
# 
# 
# temp <-as.matrix(assayDataElement(target_Data[,1:10], elt = "q_norm"))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
#     geom_boxplot(fill = "#2CA02C") +
#     scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
#     scale_x_discrete(labels=c(1:10)) +
#     labs(title="Q3 Norm Counts", x="segment", y = "Counts, Q3 Normalized")
# 
# 
# temp <-as.matrix(assayDataElement(target_Data[,1:10], elt = "neg_norm"))
# long <- melt(temp)
# colnames(long) <- c("gene","segment","count")
# ggplot(long, aes(x=segment,y=count)) +
#     geom_boxplot(fill = "#FF7F0E") +
#     scale_y_continuous(trans=scales::pseudo_log_trans(base = 10)) +
#     scale_x_discrete(labels=c(1:10)) +
#     labs(title="Neg Norm Counts", x="segment", y = "Counts, Neg. Normalized")

raw counts

boxplot(exprs(target_Data)[,1:8],
        col = "#9EDAE5", main = "Raw Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Raw")

Q3 normalized

Q3 (3rd quartile of all selected targets) is the recommended normalization method for NGS data for all targets that are above the limit of quantitation (LOQ). Q3 normalization uses the top 25% of expressers to normalize across ROIs/segments, so it is robust to changes in expression of individual genes and ideal for making comparisons across ROIs/segments.

boxplot(assayDataElement(target_Data[,1:8], elt = "q_norm"),
        col = "#2CA02C", main = "Q3 Norm Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Q3 Normalized")

Quantile normalized (testing phase)

The quantile normalization involves first ranking the gene of each sample by magnitude, calculating the average value for genes occupying the same rank, and then substituting the values of all genes occupying that particular rank with this average value. The next step is to reorder the genes of each sample in their original order2.

boxplot(assayDataElement(target_Data[,1:8], elt = "quantile_norm"),
        col = "pink", main = "Quantile Norm Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Quantile Normalized")

Negative probe normalization

For each sample, an RNA-probe-pool-specific negative probe normalization factor was generated on the basis of the geometric mean of negative probes in each pool.

boxplot(assayDataElement(target_Data[,1:8], elt = "neg_norm"),
        col = "#FF7F0E", main = "Neg Norm Counts",
        log = "y", names = 1:8, xlab = "Segment",
        ylab = "Counts, Neg. Normalized")

6 Unsupervised Analysis

6.1 UMAP

Use the tab-menu to navigate!

1

custom_umap <- umap::umap.defaults
custom_umap$random_state <- 42
# run UMAP

umap_out <-
  umap(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
       config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out$layout[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +

  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

2

ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = ANN3, shape = ANN1)) +

  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

6.2 Run tSNE

Use the tab-menu to navigate!

One common approach to understanding high-plex data is dimension reduction. Two common methods are UMAP and tSNE, which are non-orthogonally constrained projections that cluster samples based on overall gene expression. In this study, we see by either UMAP (from the umap package) or tSNE (from the Rtsne package)

1

tsne_out <-
  Rtsne(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
        perplexity = ncol(target_Data)*.15)
pData(target_Data)[, c("tSNE1", "tSNE2")] <- tsne_out$Y[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = tSNE1, y = tSNE2, shape = slide_name, color = ANN2)) +
  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

2

tsne_out <-
  Rtsne(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
        perplexity = ncol(target_Data)*.15)
pData(target_Data)[, c("tSNE1", "tSNE2")] <- tsne_out$Y[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = tSNE1, y = tSNE2, color = ANN3, shape = ANN1)) +
  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

If no bias is observed set batch correction needed on FALSE, otherwise TRUE.

batch_correction_needed = FALSE

6.3 Batch correction

If the data is observed to have a batch effect it can be corrected with the methods: RUV4, LIMMA, or Combat-Seq. Each method is done and the best one is picked and used.

The standR package is short for Spatial transcriptomics analyzes and decoding in R, it aims at providing good practice pipeline and useful functions for users to analyze Nanostring’s GeoMx DSP data. In the Nanostring’s GeoMX DSP protocol, due to the fact that one slide is only big enough for a handful of tissue segments (ROIs), it is common that we see the DSP data being confounded by the batch effect introduced by different slides. In order to establish fair comparison between ROIs later on, it is necessary to remove this batch effect from the data. (https://bioconductor.org/packages/release/bioc/vignettes/standR/inst/doc/standR_introduction.html)

For RUV4 correction, the function is requiring 3 parameters other than the input object, including factors: the factor of interest, i.e. the biological variation we plan to keep; NCGs: the list of negative control genes detected using the function findNCGs; and k: is the number of unwanted factors to use, in the RUV documentation, it is suggest that we should use the smallest k once we don’t observe technical variation in the data.

Another option is set the parameter method to “Limma”, which uses the remove batch correction method from limma. In this mode, the function is requiring 2 parameters, including batch: a vector that indicating batches for all samples; and design: a design matrix which is generated by model.matrix, in the design matrix, all biologically-relevant factors should be included.

ComBat-seq is a batch effect adjustment tool for bulk RNA-seq count data. It is an improved model based on the popular ComBat, to address its limitations through novel methods designed specifically for RNA-Seq studies. ComBat-seq takes untransformed, raw count matrix as input. Same as ComBat, it requires a known batch variable. (https://github.com/zhangyuqing/ComBat-seq)

Set up standR object

count_geomx <- as.data.frame(target_Data@assayData[["exprs"]])

sample_geomx <- target_Data@phenoData@data
sample_geomx$roi <- target_Data@protocolData@data$roi
sample_geomx <- as.data.frame(sample_geomx)
sample_geomx$Sample_ID <- rownames(sample_geomx)
sample_geomx$SegmentDisplayName <- paste(sample_geomx$`scan name`, sample_geomx$roi, sample_geomx$segment, sep = " | ")
sample_geomx$ROICoordinateX <- 1
sample_geomx$ROICoordinateY <- 1


feature_geomx <- fData(Data)
feature_geomx <- feature_geomx[c("RTS_ID", "TargetName", "ProbeID", "Negative")]
feature_geomx <- as.data.frame(feature_geomx)
rownames(feature_geomx) <- NULL

matching <- sample_geomx$SegmentDisplayName[sample_geomx$Sample_ID %in% colnames(count_geomx)]
colnames(count_geomx) <- matching
count_geomx$TargetName <- rownames(count_geomx)
rownames(count_geomx) <- NULL

spe <- readGeoMx(count_geomx, sample_geomx, featureAnnoFile = feature_geomx, hasNegProbe = TRUE)

colData(spe)$regions <- paste0(colData(spe)$ANN2,"_",colData(spe)$ANN1) |> 
  (\(.) gsub("_Geometric Segment","",.))() |>
  paste0("_",colData(spe)$pathology) |>
  (\(.) gsub("_NA","_ns",.))()

colData(spe)$regions <- paste0(colData(spe)$ANN2,"_",colData(spe)$ANN1) |> 
  (\(.) gsub("_Geometric Segment","",.))() |>
  paste0("_",colData(spe)$pathology) |>
  (\(.) gsub("_NA","_ns",.))()
colData(spe)$biology <- paste0(colData(spe)$regions)

See optimal k value for RUV4

spe <- findNCGs(spe, batch_name = "slide_name", top_n = 500)
findBestK(spe, maxK = 10, factor_of_int = "biology", NCGs = metadata(spe)$NCGs, factor_batch = "slide_name")

RUV4

ruv4 <- geomxBatchCorrection(spe, factors = "biology", 
                   NCGs = metadata(spe)$NCGs, k = 1)

plotPairPCA(ruv4, assay = 2, color = slide_name, shape = regions, title = "RUV4 removeBatch")

plotRLExpr(ruv4, assay = 2, color = `slide_name`) + ggtitle("RUV4 removeBatch")

Limma

limma <- geomxBatchCorrection(spe,
                       batch = colData(spe)$`slide_name`, method = "Limma",
                       design = model.matrix(~ 0 + ANN1 + regions, 
                                             data = colData(spe)))

plotPairPCA(limma, assay = 2, color = slide_name, shape = regions, title = "Limma removeBatch")

plotRLExpr(limma, assay = 2, color = slide_name) + ggtitle("Limma removeBatch")

CombatSeq

adjusted <- ComBat_seq(target_Data@assayData[["exprs"]], batch=target_Data@phenoData@data[["slide_name"]], group=target_Data@phenoData@data[["ANN2"]])
assayDataElement(object = target_Data, elt = "combat") <-  adjusted

umap_out2 <-
  umap(t(log2(assayDataElement(target_Data , elt = "combat"))),
       config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out2$layout[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +

  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw() + ggtitle("CombatSeq removeBatch")

Compare limma vs RUV4

spe_list <- list(spe, ruv4, limma)

plotClusterEvalStats(spe_list = spe_list,
                     bio_feature_name = "regions",
                     batch_feature_name = "slide_name",
                     data_names = c("Raw","RUV4","Limma"))

Add batch correction result to target_Data

neg_probes_save <- t(as.matrix(target_Data@assayData$q_norm["NegProbe-WTX",]))
rownames(neg_probes_save) <- "NegProbe-WTX"

# Depending on correct method, change the word "limma" to "ruv4" or vice versa.
limma <-limma@assays@data@listData[["logcounts"]]
limma <- rbind(limma, neg_probes_save)
colnames(limma) <- colnames(as.data.frame(target_Data@assayData[["q_norm"]]))
assayDataElement(object = target_Data, elt = "limma") <-  limma

ruv4 <-ruv4@assays@data@listData[["logcounts"]]
ruv4 <- rbind(ruv4, neg_probes_save)
colnames(ruv4) <- colnames(as.data.frame(target_Data@assayData[["q_norm"]]))
assayDataElement(object = target_Data, elt = "ruv4") <-  ruv4

Choose method

# choose method to replace q_norm or set it to ""
method <- ""

# replace q_norm with chosen method
if (!method == "") {
  # save orginal q_norm
  assayDataElement(object = target_Data, elt = "original") <-  assayDataElement(object = target_Data, elt = "q_norm")
  
  assayDataElement(object = target_Data, elt = "q_norm") <-  assayDataElement(object = target_Data, elt = method)
  print(paste("Batch correction method:", method))
} else {print("No batch correction needed")}
## [1] "No batch correction needed"

Umap after batch correction

umap_out <-
  umap(t(log2(assayDataElement(target_Data , elt = "q_norm"))),
       config = custom_umap)
pData(target_Data)[, c("UMAP1", "UMAP2")] <- umap_out$layout[, c(1,2)]
ggplot(pData(target_Data),
       aes(x = UMAP1, y = UMAP2, color = slide_name, shape = ANN1)) +
  geom_point(size = 3) +
  #geom_text_repel(aes(label=row.names(pData(target_Data))), size=2,max.overlaps = 100)+
  theme_bw()

6.4 Clustering high CV Genes

Another approach to explore the data is to calculate the coefficient of variation (\(CV\)) for each gene (\(g\)) using the formula \(CV_g=SD_g/mean_g\). We then identify genes with high CVs that should have large differences across the various profiled segments. This unbiased approach can reveal highly variable genes across the study.

We plot the results using unsupervised hierarchical clustering, displayed as a heatmap.

# create a log2 transform of the data for analysis
assayDataElement(object = target_Data, elt = "log_q") <-
  assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")

# create CV function
calc_CV <- function(x) {sd(x) / mean(x)}
CV_dat <- assayDataApply(target_Data,
                         elt = "log_q", MARGIN = 1, calc_CV)
# show the highest CD genes and their CV values
sort(CV_dat, decreasing = TRUE)[1:5]
##   CAMK2N1    AKR1C1      AQP2       REN     GDF15 
## 0.6344192 0.5238995 0.4752667 0.4414027 0.4276390

Table of CV values

# show the highest CD genes and their CV values
datatable(as.data.frame(CV_dat),
          extensions = 'Buttons', options = list (
            order = list(1, 'desc'),
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          ), caption = "CV values of genes" 
) %>% formatRound(columns=c("CV_dat"), digits=3)

Heatmap genes in the top 3rd of the CV values

For heatmap clustering methods see: General heatmap info: https://www.rdocumentation.org/packages/pheatmap/versions/1.0.12/topics/pheatmap Distance methods further explained: https://www.rdocumentation.org/packages/stats/versions/3.6.2/topics/dist clustering methods further explained: https://www.rdocumentation.org/packages/stats/versions/3.6.2/topics/hclust

# choose clustering methods for heatmaps
clust_method <- "average"    # options: average, ward.D, ward.D2, single, complete, mcquitty, median and centroid.
# choose clustering distance methods for heatmaps
dist_method <- "correlation" # options: correlation, euclidean, maximum, manhattan, canberra, binary and minkowski.

GOI <- names(CV_dat)[CV_dat > quantile(CV_dat, 0.75)]

pheatmap(assayDataElement(target_Data[GOI, ], elt = "log_q"),
         scale = "row",
         cutree_cols = 3,
         cutree_rows = 2,
         show_rownames = FALSE, show_colnames = TRUE,
         border_color = NA,
         drop_levels = TRUE,
         clustering_method = clust_method,
         clustering_distance_rows = dist_method,
         clustering_distance_cols = dist_method,
         breaks = seq(-3, 3, 0.05),
         color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])

assayDataElement(object = target_Data, elt = "log_q") <-  assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")
log_q <-as.data.frame(assayDataElement(target_Data, elt= "log_q"))
#batch <-as.data.frame(assayDataElement(target_Data, elt= "batch"))

6.5.0 Create subset of data

If no subset is needed set subset_needed on FALSE, otherwise TRUE.

# determine AOIs to use
subset_needed <- FALSE

#active_aois<-rownames(ann)[ann$patient=="p4"]
active_aois<- names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))

6.5.1 Clustering high CV genes for subset

Calculating CV values

# create a log2 transform of the data for analysis
assayDataElement(object = target_Data, elt = "log_q") <-
  assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")

# create CV function
calc_CV <- function(x) {sd(x) / mean(x)}
CV_dat <- assayDataApply(target_Data[,active_aois],
                         elt = "log_q", MARGIN = 1, calc_CV)

Table of CV values

# show the highest CD genes and their CV values
datatable(as.data.frame(CV_dat),
          extensions = 'Buttons', options = list (
            order = list(1, 'desc'),
            dom = 'Bftrip',
            buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
          ), caption = "CV values of genes" 
) %>% formatRound(columns=c("CV_dat"), digits=3)

Heatmap on of subset, genes in the top 3rd of the CV values

# choose clustering methods for heatmaps
clust_method <- "average"    # options: average, ward.D, ward.D2, single, complete, mcquitty, median and centroid.
# choose clustering distance methods for heatmaps
dist_method <- "correlation" # options: correlation, euclidean, maximum, manhattan, canberra, binary and minkowski.

# Identify genes in the top 3rd of the CV values
GOI <- names(CV_dat)[CV_dat > quantile(CV_dat, 0.75)]
pheatmap(assayDataElement(target_Data[GOI,active_aois ], elt = "log_q"),
        scale = "row",
        fontsize_row = 5,
        cutree_cols = 3,
        cutree_rows = 2,
        show_rownames = FALSE, show_colnames = TRUE,
        border_color = NA,
        clustering_method = clust_method,
        clustering_distance_rows = dist_method,
        clustering_distance_cols = dist_method,
        breaks = seq(-3, 3, 0.05),
        color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
       annotation_colors = color_list,
        annotation_col =
          pData(target_Data)[, ann_names])

7.1 Differential Expression

t-test

#gc()
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BY"
#options: "holm"       "hochberg"   "hommel"     "bonferroni" "BH"         "BY"         "fdr" 
counter=1

comps_df<-data.frame(comp='',val='')

for(region in unique(pData(target_Data)$ANN3)) {
  for (active_group1 in unique(pData(target_Data)$ANN1)) {
    for (active_group2 in unique(pData(target_Data)$ANN1)) {
      
      #supress reduncant compares
      if(active_group1==active_group2) {next}
      comp<-paste(sort(c(region, active_group1,active_group2)),collapse = "_")
      if(comp %in% comps_df$comp) {next}
      temp_df<-data.frame(comp=comp ,val=1)
      comps_df<-rbind(comps_df,temp_df)
      
      labels[[counter]]<-paste(active_group1," vs ", active_group2)
      group1<-log_q[,names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))[pData(target_Data)$ANN1==active_group1 & pData(target_Data)$ANN3==region]]
      group2<-log_q[,names(as.data.frame(assayDataElement(target_Data, elt= "exprs")))[pData(target_Data)$ANN1==active_group2 & pData(target_Data)$ANN3==region]]
      
      #run t_tests  
      results<-as.data.frame ( apply(log_q, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
      colnames(results)<-"raw_p_value"
      
      #multiple_testing_correction
      adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
      results<-cbind(results,adj_p_value)
      
      #calc_fdr
      FDR<- p.adjust(results$raw_p_value,method="fdr")
      results<-cbind(results,FDR)
      
      #fold_changes
      #as base data is already log transformed, means need to be subtracted to get FC in log space
      fchanges<-as.data.frame(apply(log_q, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]))))
      colnames(fchanges)<-"FC"
      #paste("FC",active_group1," / ",active_group2)
      results<-cbind(results,fchanges)
      
      #add genenames
      results$Gene<-rownames(results)
      
      #set categories based on P-value & FDR for plotting
      results$Color <- "NS or FC < 1"
      results$Color[results$raw_p_value < 0.05] <- "P < 0.05"
      results$Color[results$FDR < 0.05] <- "FDR < 0.05"
      results$Color[results$FDR < 0.001] <- "FDR < 0.001"
      results$Color[abs(results$FC) <1] <- "NS or FC < 1"
      results$Color <- factor(results$Color,
                              levels = c("NS or FC < 1", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))
      
      #vulcanoplot
      
      # pick top genes for either side of volcano to label
      # order genes for convenience:
      
      results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
      top_g <- c()
      top_g <- c(top_g,
                 results[, 'Gene'][
                   order(results[, 'invert_P'], decreasing = TRUE)[1:20]],
                 results[, 'Gene'][order(results[, 'invert_P'], decreasing = FALSE)[1:20]])
      top_g<- unique(top_g)
      results <- results[, -1*ncol(results)] # remove invert_P from matrix
      
      # Graph results
      
      plots[[counter]]<- ggplot(results,
                                      aes(x = FC, y = -log10(raw_p_value),
                                          color = Color, label = Gene)) +
        geom_vline(xintercept = c(1, -1), lty = "dashed") +
        geom_hline(yintercept = -log10(0.05), lty = "dashed") +
        geom_point() +
        labs(x = paste("Enriched in", active_group2," <- log2(FC) -> Enriched in", active_group1),
             y = "Significance, -log10(P)",
             color = "Significance") +
        scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                      `FDR < 0.05` = "lightblue",
                                      `P < 0.05` = "orange2",
                                      `NS or FC < 0.5` = "gray"),
                           guide = guide_legend(override.aes = list(size = 4))) +
        scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
        geom_text_repel(data = subset(results, (-1>FC| FC>1) & FDR < 0.05 & Gene %in% top_g),
                        point.padding = 0.15, color = "black", size=3.5,
                        min.segment.length = .1, box.padding = .2, lwd = 2,
                        max.overlaps = 50) +
        theme_bw(base_size = 10) +
        theme(legend.position = "bottom") +
        ggtitle(paste("class: ", region," - ", test, mtc,"multitest corr"))
      
      #store tables for display later
      tables[[counter]]<-results
      
      counter = counter+1
      #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
    }
  }
}
grid.arrange(grobs=plots,ncol=2)

#strangly does not appear in html output -> chucks are limited to one datatable per chuck. The htmltools are a way around this but does show them stacked in a box.
results_tables <- htmltools::tagList()
for (c in (2:counter-1)) {
  #Gene %in% GOI
  results_tables[[c]] <- datatable(subset(tables[[c]], Color == c("FDR < 0.001",  "P < 0.05")), 
           rownames=FALSE,
           extensions = c('Buttons'), options = list (
              dom = 'Bftrip',
              buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
            ), height = 550,
           caption = paste("DE results ", labels[[c]]),filter='top') %>% formatRound(columns=c("raw_p_value","adj_p_value","FDR","FC"), digits=3)
  #cat('\n\n<!-- -->\n\n')
}            
results_tables
print("") # for layout of next section
## [1] ""

7.2 DE analysis with LMM

A common statistical approach is to use a linear mixed-effect model (LMM). The LMM allows the user to account for the subsampling per tissue; in other words, we adjust for the fact that the multiple regions of interest placed per tissue section are not independent observations, as is the assumption with other traditional statistical tests. The formulation of the LMM model depends on the scientific question being asked.

Overall, there are two flavors of the LMM model when used with GeoMx data: i) with and ii) without random slope.

When comparing features that co-exist in a given tissue section, a random slope is included in the LMM model. When comparing features that are mutually exclusive in a given tissue section the LMM model does not require a random slope.

Mostly, we use patient/sample as Random Intercept when they are combined on slides.

# convert test variables to factors
pData(target_Data)$testClass <- factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))
pData(target_Data)[["slide"]] <- factor(pData(target_Data)[["slide_name"]])
#assayDataElement(object = target_Data, elt = "log_q") <- assayDataApply(target_Data, 2, FUN = log, base = 2, elt = "q_norm")

# run LMM:
# formula follows conventions defined by the lme4 package
lmm_results <- c()
for (region in unique(pData(target_Data)$ANN3)) {
    ind <- pData(target_Data)$ANN3 == region

    mixedOutmc <-
        mixedModelDE(target_Data[,ind],
                     elt = "log_q",
                     modelFormula = ~ testClass + (1 | slide),
                     groupVar = "testClass",
                     nCores = parallel::detectCores(),
                     multiCore = TRUE)

    # format results as data.frame
    r_test <- do.call(rbind, mixedOutmc["lsmeans", ])
    tests <- rownames(r_test)
    r_test <- as.data.frame(r_test)
    r_test$Contrast <- tests

    # use lapply in case you have multiple levels of your test factor to
    # correctly associate gene name with it's row in the results table
    r_test$Gene <-
        unlist(lapply(colnames(mixedOutmc),
                      rep, nrow(mixedOutmc["lsmeans", ][[1]])))
    r_test$Subset <- region
    r_test$FDR <- p.adjust(r_test$`Pr(>|t|)`, method = "fdr")
    r_test <- r_test[, c("Gene", "Subset", "Contrast", "Estimate",
                         "Pr(>|t|)", "FDR")]
    
    lmm_results <- rbind(lmm_results, r_test)
}

7.3 Vulcanoplot + table of LMM

# Categorize Results based on P-value & FDR for plotting
fc_threshold = 1

lmm_results$Color <- paste("NS or FC < ",fc_threshold = 1)
lmm_results$Color[lmm_results$`Pr(>|t|)` < 0.05] <- "P < 0.05"
lmm_results$Color[lmm_results$FDR < 0.05] <- "FDR < 0.05"
lmm_results$Color[lmm_results$FDR < 0.001] <- "FDR < 0.001"
lmm_results$Color[abs(lmm_results$Estimate) < fc_threshold] <- paste("NS or FC < ",fc_threshold = 1)
lmm_results$Color <- factor(lmm_results$Color,
                        levels = c("NS or FC < 1", "P < 0.05",
                                   "FDR < 0.05", "FDR < 0.001"))
counter = 1
plots_lmm <- list()
for(c in unique(lmm_results$Subset) ) {
  lmm_results_slice = lmm_results[lmm_results$Subset == c,]

  # pick top genes for either side of volcano to label
  # order genes for convenience:
  lmm_results_slice$invert_P <- (-log10(lmm_results_slice$`Pr(>|t|)`)) * sign(lmm_results_slice$Estimate)
  
  #loop here over tested conditions if applicable
  top_g <- c()
  top_g <- c(top_g,
    lmm_results_slice[, 'Gene'][
      order(lmm_results_slice[, 'invert_P'], decreasing = TRUE)[1:15]],
    lmm_results_slice[, 'Gene'][
      order(lmm_results_slice[, 'invert_P'], decreasing = FALSE)[1:15]])

  top_g <- unique(top_g)
  
  lmm_results_slice <- lmm_results_slice[, -1*ncol(lmm_results_slice)] # remove invert_P from matrix
  
  # Flip Contrast
  contrast_lab <- as.character(lmm_results_slice$Contrast)
  contrast_lab <- strsplit(contrast_lab, "-")[[1]]
  contrast_lab <- paste(contrast_lab[2], "-", contrast_lab[1])
  
  # Graph results
  plots_lmm[[counter]] <- ggplot(lmm_results_slice,
               aes(x = Estimate, y = -log10(`Pr(>|t|)`),
                   color = Color, label = Gene)) +
          geom_vline(xintercept = c(fc_threshold, -fc_threshold), lty = "dashed") +
          geom_hline(yintercept = -log10(0.05), lty = "dashed") +
          geom_point() +
          labs(
            x = paste(contrast_lab, " log2(FC)"),
               y = "Significance, -log10(P)",
               color = "Significance") +
          scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                        `FDR < 0.05` = "lightblue",
                                        `P < 0.05` = "orange2",
                                        `NS or FC < 1` = "gray"),
                             guide = guide_legend(override.aes = list(size = 4))) +
          scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
          geom_text_repel(data = subset(lmm_results_slice,  `Pr(>|t|)` < 0.001 & abs(lmm_results_slice$Estimate) > fc_threshold & Gene %in% top_g),
                          point.padding = 0.15, color = "black",size=5,
                          min.segment.length = .1, box.padding = .2, lwd = 2,
                          max.overlaps = 50) +
          theme_bw(base_size = 16) +
          theme(legend.position = "bottom") +
          facet_wrap(~Subset, scales = "free_y")
  counter <- counter + 1
}
grid.arrange(grobs=plots_lmm,ncol=2)

#subset(lmm_results, Gene %in% GOI)
datatable(lmm_results, rownames = FALSE,
          extensions = 'Buttons', options = list (
              dom = 'Bftrip',
              buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
            ),
          caption = "DE results for Genes of Interest (>75% CV)",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=3)

7.4 Plotting Genes of Interest

my_gois <-unique(subset(lmm_results, `FDR` < 0.001)$Gene)
tmp_tbl<-subset(lmm_results, Gene %in% my_gois)

my_gois <- c()
for (contrast in unique(tmp_tbl$Contrast)) { # gene of interest for all contrasts
#for (contrast in c("Jux_Glo - Pro_Tub")) {
  ind <- tmp_tbl$Contrast == contrast
  goi <- tmp_tbl[ind, "Gene"][order(tmp_tbl[ind, "Estimate"], decreasing = FALSE)[1:3]]
  my_gois <- c(my_gois, goi)
  goi <- tmp_tbl[ind, "Gene"][order(tmp_tbl[ind, "Estimate"], decreasing = TRUE)[1:3]]
  my_gois <- c(my_gois, goi)
}

if(nrow(tmp_tbl) > 1) { 
  datatable(tmp_tbl,rownames = FALSE,caption = "DE results for Genes of Interest ",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=3)
 
for (my_goi in my_gois) {
# show expression for a single target
  a<-ggplot(pData(target_Data),
       aes(x = ANN2, fill = ANN2,
           y = as.numeric(assayDataElement(target_Data[my_goi, ], elt = "q_norm")))) +
  geom_violin() +
  geom_jitter(width = .2) +
  labs(y = paste(my_goi,"Expression")) +
  scale_y_continuous(trans = "log2") +
  #facet_wrap(~ANN3, nrow=1) + theme_bw(base_size = 16) +
  theme_bw() + coord_flip()
  print(a)
}
}else{
  print("No significant lMM results to plot")
}

7.5 Heatmap of Significant Genes

In addition to generating individual gene box plots or volcano plots, we can again create a heatmap from our data. This time rather than utilizing CV to select genes, we can use the P-value or FDR values to select genes. Here, we plot all genes with an FDR < 0.001.

my_gois <-unique(subset(lmm_results, `FDR` < 0.001)$Gene)   # 100 to prevent long runtime

if(length(my_gois)<2) {
  print("No significant results to show")
 
}else{

pheatmap(log2(assayDataElement(target_Data[my_gois, ], elt = "q_norm")),
         scale = "row",
         show_rownames = TRUE, show_colnames = TRUE,
         border_color = NA,
         clustering_method = "average",
         clustering_distance_rows = "correlation",
         clustering_distance_cols = "correlation",
         cutree_cols = 3, cutree_rows = 2,
         breaks = seq(-3, 3, 0.05),
         color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])
}

8 Pathway Analysis

Pathway analysis enables exploration of different aggregate gene sets for our experimental questions. Each individual ROI/AOI segment is scored for every pathway of interest, which we can then use to investigate biological differences. We will perform analogous analyses as those outlined in the Differential Expression and Spatial Deconvolution sections of the report for gene set enrichment.

8.1 Scoring Gene Sets

Pathways and gene sets were defined from the Kegg Brite database. We use an R software package called Gene Set Variation Analysis to score each segment within our study. see https://cran.r-project.org/web/packages/msigdbr/vignettes/msigdbr-intro.html for options on collections. We use the KEGG and REACTOME.

#gc()
h_gene_sets = rbind(msigdbr(species = "human", subcategory = "CP:KEGG"),
                    msigdbr(species = "human", subcategory = "CP:REACTOME"))
#msigdbr(species = "human", subcategory = "CP:BIOCARTA")

msigdbr_list = split(x = h_gene_sets$gene_symbol, f = h_gene_sets$gs_name)

# prepare df for accurate merging back genes later
pw_gene_df<-data.frame(Pathway = names(msigdbr_list))
pw_gene_df$genes<-msigdbr_list
ssgsea_results <- GSVA::gsva(expr = assayDataElement(target_Data,
                            elt = "log_q"),
                            gset.idx.list = msigdbr_list,
                            method = "ssgsea",
                            min.sz = 5,
                            max.sz = 500,
                            verbose = FALSE)
## [1] "Calculating ranks..."
## [1] "Calculating absolute values from ranks..."
## [1] "Normalizing..."
geneSetObj <-
  NanoStringGeoMxSet(assayData = ssgsea_results,
                     phenoData = AnnotatedDataFrame(pData(target_Data)),
                     protocolData = protocolData(target_Data),
                     featureType = "GeneSet",
                     check = FALSE)

8.2 Differental analysis of pathways

# # convert test variables to factors
pData(target_Data)$testClass <- factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))
pData(geneSetObj)[["slide"]]<-factor(pData(geneSetObj)[["slide_name"]])
#pData(target_Data)$testRegion<-factor(pData(target_Data)$ANN2, unique(count_mat$ANN3))

lmm_ssgsea_results <- c()
for (region in unique(pData(target_Data)$ANN3)) {
  ind <- pData(target_Data)$ANN3 == region
  mixedOutmc <-
    mixedModelDE(geneSetObj[, ind],
                 elt = "exprs",
                 modelFormula = ~ testClass + (1 | slide),
                 groupVar = "testClass",
                 nCores = parallel::detectCores(),
                 multiCore = TRUE)

  # format results as data.frame
  r_ssgsea_test <- do.call(rbind, mixedOutmc["lsmeans", ])
  ssgsea_tests <- rownames(r_ssgsea_test)
  r_ssgsea_test <- as.data.frame(r_ssgsea_test)
  r_ssgsea_test$Contrast <- ssgsea_tests
  #r_ssgsea_test$Genes <- msigdbr_list seems unreliable as gsva omits pathways if genes are not in data..

  # use lapply in case you have multiple levels of your test factor to
  # correctly associate gene name with it's row in the results table
  r_ssgsea_test$Pathway <-
    unlist(lapply(colnames(mixedOutmc),
                  rep, nrow(mixedOutmc["lsmeans", ][[1]])))

  #r_ssgsea_test$Subset <- status
  r_ssgsea_test$FDR <- p.adjust(r_ssgsea_test$`Pr(>|t|)`, method = "fdr")
  r_ssgsea_test$Subset <- region
  r_ssgsea_test <- r_ssgsea_test[, c("Pathway", "Subset", "Contrast", "Estimate",
                                     "Pr(>|t|)", "FDR")]
  lmm_ssgsea_results <- rbind(lmm_ssgsea_results, r_ssgsea_test)

  }
  lmm_ssgsea_results <- merge(lmm_ssgsea_results, pw_gene_df)

8.2.1 Table of Differental analysis of pathways

datatable(subset(lmm_ssgsea_results), rownames = FALSE,
          extensions = 'Buttons', options = list (
             pageLength = 10,
              dom = 'Bftrip',
              buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
            ),
          caption = "DE results for Pathways",filter='top') %>% formatRound(columns=c("Estimate","Pr(>|t|)","FDR"), digits=5)

8.3 Vulcanoplot of LMM_Pathways

# Categorize Results based on P-value & FDR for plotting
fc_threshold = 0.3

lmm_ssgsea_results$Color <- "NS or FC < 0.3"
lmm_ssgsea_results$Color[lmm_ssgsea_results$`Pr(>|t|)` < 0.05] <- "P < 0.05"
lmm_ssgsea_results$Color[lmm_ssgsea_results$FDR < 0.05] <- "FDR < 0.05"
lmm_ssgsea_results$Color[lmm_ssgsea_results$FDR < 0.001] <- "FDR < 0.001"
lmm_ssgsea_results$Color[abs(lmm_ssgsea_results$Estimate) < fc_threshold] <- "NS or FC < 0.3"
lmm_ssgsea_results$Color <- factor(lmm_ssgsea_results$Color,
                        levels = c("NS or FC < 0.3", "P < 0.05",
                                   "FDR < 0.05", "FDR < 0.001"))

# pick top pw for either side of volcano to label
# order pw for convenience:
lmm_ssgsea_results$invert_P <- (-log10(lmm_ssgsea_results$`Pr(>|t|)`)) * sign(lmm_ssgsea_results$Estimate)
top_ssgsea_g <- c()

#loop here over tested conditions if applicable

for(c in unique(lmm_ssgsea_results$Subset) ) {
  lmm_results_slice = lmm_ssgsea_results[lmm_ssgsea_results$Subset == c,]
 top_ssgsea_g <- c(top_ssgsea_g,
         lmm_ssgsea_results[ind, 'Pathway'][
               order(lmm_ssgsea_results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
         lmm_ssgsea_results[ind, 'Pathway'][
               order(lmm_ssgsea_results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
}
 
top_ssgsea_g <- unique(top_ssgsea_g)
lmm_ssgsea_results <- lmm_ssgsea_results[, -1*ncol(lmm_ssgsea_results)] # remove invert_P from matrix

#lmm_ssgsea_results_slice <- lmm_ssgsea_results_slice[lmm_ssgsea_results_slice$FDR < 1,]

# Graph results
ggplot(lmm_ssgsea_results,
       aes(x = Estimate, y = -log10(`Pr(>|t|)`),
           color = Color, label = Pathway)) +
    geom_vline(xintercept = c(0.2, -0.2), lty = "dashed") +
    geom_hline(yintercept = -log10(0.05), lty = "dashed") +
    geom_point() +
    labs(x = paste(lmm_results_slice$Contrast, "log2(FC)"),
         y = "Significance, -log10(P)",
         color = "Significance") +
    scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                  `FDR < 0.05` = "lightblue",
                                  `P < 0.05` = "orange2",
                                  `NS or FC < 0.5` = "gray"),
                       guide = guide_legend(override.aes = list(size = 4))) +
    scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
    geom_text_repel(data = subset(lmm_ssgsea_results, Color == "FDR < 0.05" | Color == "FDR < 0.001"),
                   point.padding = 0.15, color = "black",size=3,
                   min.segment.length = .1, box.padding = .2, lwd = 2,
                   max.overlaps = 50) +
    theme_bw(base_size = 16) +
    theme(legend.position = "bottom") +
    facet_wrap(~Subset, scales = "free_y")

#    +facet_wrap(~Subset, scales = "free_y"))

8.4 heatmap of pathways

  active_pw<-filter(lmm_ssgsea_results, `Pr(>|t|)` < 0.001)$Pathway
  active_pw<-filter(lmm_ssgsea_results, FDR < 0.001 )$Pathway
  #active_pw<-filter(lmm_ssgsea_results, Color == "FDR < 0.001")$Pathway
  
  
  active_pw<-top_ssgsea_g
  
  if (length(active_pw)>1) {
  
    print("go")
    
  pw_matrix<-assayDataElement(geneSetObj, elt = "exprs")

  pheatmap(pw_matrix[active_pw,],
         scale = "row",
         show_rownames = TRUE, show_colnames = TRUE,
         fontsize_row = 10,
         border_color = NA,
         clustering_method = "average",
         #clustering_distance_rows = "correlation",
         clustering_distance_cols = "euclidean",
         cutree_cols = 2, cutree_rows = 2,
         breaks = seq(-3, 3, 0.05),
         #color = colorRampPalette(c("purple3", "black", "yellow2"))(120),
         main = "Heatmap of selected Pathways",
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])
  
  }else{
    print("No significant results to display")
  }
## [1] "go"

8.5 fgsea pathway analysis

Another option for pathway analysis is the R software package called Fast Gene Set Enrichment Analysis. It is a pre-ranked method that uses the LMM results combined with the same pathway list from msigdbr as GSVA.

fgsea_results_all <- c()
for (contrast in unique(lmm_results$Contrast)) {
    
    ranks <- lmm_results$Estimate[lmm_results$Contrast == contrast]
    names(ranks) <- lmm_results$Gene[lmm_results$Contrast == contrast]
    
    fgsea_results <- fgsea(msigdbr_list, 
                         ranks, 
                         eps = 0.0,
                         minSize=15, 
                         maxSize = 500)
    fgsea_results$Contrast <- contrast
    fgsea_results_all <- rbind(fgsea_results_all, fgsea_results)
    
}

fgsea_reactome <- fgsea_results_all[grep("REACTOME", pathway),]
fgsea_reactome$pathway <- gsub("REACTOME_", "", fgsea_reactome$pathway)
fgsea_reactome$pathway <- gsub("_", " ", fgsea_reactome$pathway)
# Categorize Results based on P-value & FDR for plotting
fc_threshold = 0.3


fgsea_results_all$Color <- "NS or FC < 0.3"
fgsea_results_all$Color[fgsea_results_all$pval < 0.05] <- "P < 0.05"
fgsea_results_all$Color[fgsea_results_all$padj < 0.05] <- "FDR < 0.05"
fgsea_results_all$Color[fgsea_results_all$padj < 0.001] <- "FDR < 0.001"
fgsea_results_all$Color[abs(fgsea_results_all$ES) < fc_threshold] <- "NS or FC < 0.3"
fgsea_results_all$Color <- factor(fgsea_results_all$Color,
                        levels = c("NS or FC < 0.3", "P < 0.05",
                                   "FDR < 0.05", "FDR < 0.001"))

plt <- htmltools::tagList()
counter = 1
for(c in unique(fgsea_results_all$Contrast) ) {
  fgsea_results_slice = fgsea_results_all[fgsea_results_all$Contrast == c,]

  # # pick top genes for either side of volcano to label
  # # order genes for convenience:
  fgsea_results_slice$invert_P <- (-log10(fgsea_results_slice$pval)) * sign(fgsea_results_slice$ES)
  
  # #loop here over tested conditions if applicable
  #top_fgsea_g <- c()
  top_fgsea_g <- c(fgsea_results_slice[, 'pathway'][
                        order(fgsea_results_slice[, 'invert_P'], decreasing = TRUE)[1:15]],
                    fgsea_results_slice[, 'pathway'][
                        order(fgsea_results_slice[, 'invert_P'], decreasing = FALSE)[1:15]])
   
  # for (celltype in unique(lmm_results$Subset)) {
  #      top_fgsea_g <- c(top_fgsea_g,
  #                  fgsea_results_slice[fgsea_results_slice$Subset==celltype, 'pathway'][
  #                      order(fgsea_results_slice[, 'invert_P'], decreasing = TRUE)[1:20]],
  #                  fgsea_results_slice[fgsea_results_slice$Subset==celltype, 'pathway'][
  #                      order(fgsea_results_slice[, 'invert_P'], decreasing = FALSE)[1:20]])
  # }
  
  top_fgsea_g <- unique(top_fgsea_g)
  #fgsea_results_slice <- fgsea_results_slice[, -1*ncol(fgsea_results_slice)] # remove invert_P from matrix
  
  # Graph results
  dynplot <- ggplot(fgsea_results_slice,
         aes(x = ES, y = -log10(pval),
             color = Color, label = pathway)) +
      geom_vline(xintercept = c(fc_threshold,-fc_threshold), lty = "dashed") +
      geom_hline(yintercept = -log10(0.05), lty = "dashed") +
      geom_point() +
      labs(x = paste(c, " FC"),
           y = "Significance, -log10(P)",
           color = "Significance") +
      scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                    `FDR < 0.05` = "lightblue",
                                    `P < 0.05` = "orange2",
                                    `NS or FC < 0.3` = "gray"),
                         guide = guide_legend(override.aes = list(size = 4))) +
      scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
      geom_text_repel(data = subset(fgsea_results_slice, Color == "P < 0.05" | Color == "FDR < 0.05" | Color == "FDR < 0.001"),
                     point.padding = 0.15, color = "black",size=5,
                     min.segment.length = .1, box.padding = .2, lwd = 2,
                     max.overlaps = 50) +
      theme_bw(base_size = 16) +
      theme(legend.position = "bottom") #+
      #facet_wrap(~Subset, scales = "free_y")
   
  plt[[counter]] <- as_widget(ggplotly(dynplot))
  counter <- counter + 1
  #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
}
plt

Barplot pathways

Clear vizualization of the top 15 positive and negative expressed Reactome pathways. Pathways with a adjusted p-value of 0.05 or higher will be presented as red and below 0.05 as green.

Fgsea returns results from the given databases. To avoid one database overshadowing the other, results have been split to show database specific results. Reactome is shown in barplots and KEGG as a pathway image including highlighted genes.

top_all <- c()
for (contrast in unique(fgsea_reactome$Contrast)) {
    # active_group1 <- contrast_list[contrast][[1]][[1]]
    # active_group2 <- contrast_list[contrast][[1]][[2]]
    # ind <- pData(target_Data)[pData(target_Data)$ANN2 == active_group1 | pData(target_Data)$ANN2 == active_group2]
    top <- fgsea_reactome[fgsea_reactome$Contrast == contrast]
    toppos <- top[order(top$NES, decreasing = TRUE)][0:15]
    topneg <- top[order(top$NES, decreasing = FALSE)][0:15]
    top <- rbind(toppos, topneg)
    
    top$Color[top$padj < 0.05] <- "padj < 0.05"
    top$Color[top$padj == 0.05 | top$padj > 0.05] <- "padj > 0.05"
    top_all <- rbind(top_all, top)
    barplot <- ggplot(top,aes(x=reorder(pathway, NES),y=NES,fill=Color)) + 
      scale_x_discrete(labels = function(x) str_wrap(x, width = 100)) + 
      geom_col(position="dodge") + scale_fill_manual(values = c("#2ECC71", "#E74C3C")) +
      ggtitle(paste("Top 15 + and - NES reactome pathways from", contrast)) + 
      xlab("pathway") + ylab("Normalized Enrichment Score") +
      coord_flip() + 
      theme(text = element_text(size = 20)) +
      theme(panel.background = element_blank())
      #facet_wrap(~Contrast, scales = "free_y")
    print(barplot)
}

8.6 Visualize KEGG pathway

Get pathway visualization from KEGG. clusterProfiler grabs the KEGG pathway IDs and combined with pathview it can download a pathway image from the KEGG database. The LMM results are given with the pathway id to visualize up and down regulated genes. It will pick the most significant pathway if not manually specified.

get_wp_gmtfile <- function() {
    wpurl <- 'https://wikipathways-data.wmcloud.org/current/gmt/'
    x <- readLines(wpurl)
    y <- x[grep('\\.gmt',x)]
    sub(".*(wikipathways-.*\\.gmt).*", "\\1",  y[grep('File', y)])
}

get_wp_data <- function(organism) {
    organism <- sub(" ", "_", organism)
    gmtfile <- get_wp_gmtfile()
    wpurl <- 'https://wikipathways-data.wmcloud.org/current/gmt/'
    url <- paste0(wpurl,
                  gmtfile[grep(organism, gmtfile)])
    f <- tempfile(fileext = ".gmt")
    dl <- mydownload(url, destfile = f)
    if (is.null(f)) {
        message("fail to download wikiPathways data...")
        return(NULL)
    }
    read.gmt.wp(f)
}
# enrichKEGG grab online pathways
gene <-  target_Data@featureData@data[["GeneID"]]
kk <- enrichKEGG(gene         = gene,
                 organism     = 'hsa',
                 pvalueCutoff = 0.1)
## Reading KEGG annotation online: "https://rest.kegg.jp/link/hsa/pathway"...
## Reading KEGG annotation online: "https://rest.kegg.jp/list/pathway/hsa"...
enrichKEGG_results <- kk@result

# Match column values for link
# fsgea run
fgsea_results$Description <- gsub("KEGG_", "", fgsea_results$pathway)
fgsea_results$Description <- gsub("_", " ", fgsea_results$Description)
# ssgea run
# lmm_ssgsea_results_d$Description <- gsub("KEGG_", "", lmm_ssgsea_results_d$Pathway)
# lmm_ssgsea_results_d$Description <- gsub("_", " ", lmm_ssgsea_results_d$Description)

enrichKEGG_results$Description <- toupper(enrichKEGG_results$Description)

# Create df for matching KEGG results
KEGGIDs <- fgsea_results#[fgsea_results$Contrast == "Pro_Tub - Dis_Tub"]
KEGGIDs <- KEGGIDs %>% inner_join(enrichKEGG_results, by = 'Description') %>% select(Description, ID, geneID, leadingEdge, padj)

# Create geneList with DE results for visualization pathway genes 
genelist <- lmm_results$Estimate
names(genelist) <- target_Data@featureData@data[["GeneID"]]

# Choose manual or highest significant pathway
pathway_name <- "INSULIN SIGNALING PATHWAY" # manual pathway query
pathway_name <- toupper(pathway_name)
if (pathway_name == "") {
  pathway_name <- KEGGIDs[order(KEGGIDs$padj, decreasing = FALSE), ][1]$Description
}
pathwayid <- KEGGIDs$ID[KEGGIDs$Description == pathway_name]

# Grab pathway image with gene info, save and plot
viewPath <- pathview(gene.data  = genelist,
                     pathway.id = pathwayid,
                     species    = "hsa",
                     out.suffix = "genesinfo", kegg.native = T, same.layer = F)
## 'select()' returned 1:1 mapping between keys and columns
## Info: Working in directory C:/Users/pkloosterman/Documents/GitHub/GeoMX-analysis/Data/DKD_Kidney
## Info: Writing image file hsa04910.genesinfo.png
img <- readPNG(paste(pathwayid, ".genesinfo.png", sep = ""))
grid::grid.raster(img)

9 Spatial Deconvolution

9.1 Calculate backgrounds

#gc()
bg = derive_GeoMx_background(
  norm = assayDataElement(target_Data , elt = "q_norm"),
  probepool = fData(target_Data)$Module,
  negnames = c("NegProbe-CTP01","NegProbe-Kilo","Negative Probe", "NegProbe-WTX" ))
  #negnames = "NegProbe-WTX")

9.2 Load cell profile

A “cell profile matrix” is a pre-defined matrix that specifies the expected expression profiles of each cell type in the experiment. The SpatialDecon library comes with one such matrix pre-loaded, the “SafeTME” matrix, designed for estimation of immune and stroma cells in the tumor microenvironment. (This matrix was designed to avoid genes commonly expressed by cancer cells; see the SpatialDecon manuscript for details.). Otherwise, load specific profiles from https://github.com/Nanostring-Biostats/CellProfileLibrary/tree/NewProfileMatrices

#safeTME
data("safeTME")
data("safeTME.matches")
current_cell_profile<-safeTME

#see: https://github.com/Nanostring-Biostats/CellProfileLibrary/tree/NewProfileMatrices

current_cell_profile <- download_profile_matrix(species = "Human",
                                               age_group = "Adult",
                                               matrixname = "Kidney_HCA")

heatmap(sweep(current_cell_profile, 1, apply(current_cell_profile, 1, max), "/"),
        labRow = NA, margins = c(10, 5), cexCol = 0.7)

9.3 Run spatial deconvolution

# vector identifying pure tumor segments:
#target_Data$istumor = target_Data$ANN3 == "CORE" & target_Data$ANN1 == "PanCK+"
res = runspatialdecon(object = target_Data,
                      norm_elt = "q_norm",
                      raw_elt = "exprs",
                      #is_pure_tumor = target_Data$istumor,
                      cell_counts = target_Data$nuclei,
                      X = current_cell_profile,
                      #cellmerges = safeTME.matches,              # safeTME.matches object, used by default
                      #n_tumor_clusters = 5,                      # how many distinct tumor profiles to append to safeTME
                      align_genes = TRUE)

9.3.1 Spatial deconvolution heatmaps

Abundance

# NOTE: check clustering.. why different?

#set display thresholds
thresh <- signif(quantile(res$beta, 0.97), 2)

# plot stored to keep clustering for later
p1<-pheatmap(pmin(t(res$beta),thresh),
         #scale = "row", 
         cutree_cols = 3,
         cutree_rows = 2,
         fontsize_row = 12,
         show_rownames = TRUE, show_colnames = TRUE,
         angle_col = "90",
         border_color = NA,
         #clustering_method = "average",
         #clustering_distance_rows = "correlation",
         #clustering_distance_cols = "correlation",
         legend_breaks = c(round(seq(0, thresh, length.out = 5))[-5], thresh),
         legend_labels = c(round(seq(0, thresh, length.out = 5))[-5], paste0("Abundance scores,\ntruncated above at ", thresh)),
         #breaks = seq(0, 5, 1),
         color = colorRampPalette(c("white","darkblue"))(100),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names]
         )

#p1

Proportional

# proportions:
props <- replace(res$prop_of_nontumor, is.na(res$prop_of_nontumor), 0)

p2<-pheatmap(t(props),
         #scale = "row", 
         cutree_cols = 3,
         cutree_rows = 2,
         fontsize_row = 12,
         show_rownames = TRUE, show_colnames = TRUE,
         angle_col = "90",
         border_color = NA,
         #clustering_method = "average",
         #clustering_distance_rows = "correlation",
         #clustering_distance_cols = "correlation",
         legend_breaks = round(seq(0, max(props) * 0.99, length.out = 5), 2),
         legend_labels = c(round(seq(0, max(props), length.out = 5), 2)[-5], "Proportion of all\nfitted populations"),
         color = colorRampPalette(c("white","darkblue"))(100),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])

#p2

Scaled

# scaled abundances:
epsilon <- min(res$beta[res$beta > 0])
mat <- sweep(res$beta, 1, pmax(apply(res$beta, 1, max), epsilon), "/")

pheatmap(t(mat),
         #scale = "row",
         cutree_cols = 3,
         cutree_rows = 3,
         fontsize_row = 12,
         show_rownames = TRUE, show_colnames = TRUE,
         angle_col = "90",
         border_color = NA,
         #clustering_method = "average",
         #clustering_distance_rows = "correlation",
         #clustering_distance_cols = "correlation",
         legend_breaks = c(round(seq(0, 1, length.out = 5), 2)[-5], 1),
         legend_labels = c(round(seq(0, 1, length.out = 5), 2)[-5], "Scaled abundance\n(ratio to max)"),
         color = colorRampPalette(c("white","darkblue"))(100),
         annotation_colors = color_list,
         annotation_col = pData(target_Data)[, ann_names])

9.4 Barplots

abundance

# define variables to show in heatmaps:
pData(target_Data)$region <- 
    factor(pData(target_Data)$ANN3, unique(target_Data$ANN3))   # Must be manual?
pData(target_Data)$class <- 
    factor(pData(target_Data)$ANN1, unique(target_Data$ANN1))   # Must be manual?

variables_to_plot <- c("slide_name", "ANN1", "ANN3")                     # Must be manual?

#define colors

# only for safeTME colors
#col <- cellcols

#custom celmatrix
#get large number of colors
qual_col_pals = brewer.pal.info[brewer.pal.info$category == 'qual',]
col_vector = unique(unlist(mapply(brewer.pal, qual_col_pals$maxcolors, rownames(qual_col_pals))))
celltypes<-sample(col_vector,length(colnames(res$beta)))
names(celltypes)<-colnames(res$beta)
col<-celltypes


#tempfix for abbreviated and now mismatching annotations
#can just use ann dataframe?
#tmpann<-cbind(ANN1,ANN2,SN)
tmpann <- pData(target_Data)[ann_names]



layout(matrix(c(1, 2, 3, 3), nrow = 2),
       widths = c(10, 3, 10, 3),
       heights = c(1, 8, 10),
      )

par(mar = c(0, 8.2, 0, 0.2))
plot(p1$tree_col, labels = F, main = "", ylab = "", yaxt = "n")
par(mar = c(15, 8, 0, 0))

# data to plot:
mat <- t(res$beta)[, p1$tree_col$order]
# infer scale of negative y-axis for annotation colorbars
ymin <- -max(colSums(mat)) * 0.15
if (!is.finite(ymin)) {
  ymin <- 0
}

# draw barplot:
bp <- barplot(mat,
              cex.lab = 1.5,
              col = col, border = NA,
              cex.names = 1.1,
              las = 2, main = "", ylab = "Abundance scores",
              ylim = c(ymin, max(colSums(mat)))
)


# add color bars for annotations
for (name in rev(variables_to_plot)) {
  yrange <- seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 1)[match(name, variables_to_plot) + c(0, 1)]
  xwidth <- (bp[2] - bp[1]) / 2
  for (i in 1:ncol(mat)) {
    rect(bp[i] - xwidth, yrange[2], bp[i] + xwidth, yrange[1],
         # border = NA, col = ann_colors[[name]][segmentAnnotations[match(colnames(mat)[i], segmentAnnotations$segmentID), name]]
         #border = NA, col = ann_colors[[name]][ann[p1$tree_col$order[i], name]]
         border = NA, col = color_list[[name]][tmpann[colnames(mat)[i], name]]
    )
  }
}
axis(2,
     at = seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 2)[-c(1, length(variables_to_plot) + 2)],
     las = 2, labels = variables_to_plot, lty = 0, cex.axis = 1.2
)

#draw a legend:
par(mar = c(0.1, 0.1, 0.1, 0.1))
frame()
legendcols <- legendnames <- c()
#for (name in rev(names(ann_colors))) {
for (name in c("slide_name", "ANN1", "ANN3")) {
  legendcols <- c(legendcols, NA, color_list[[name]], NA)
  legendnames <- c(legendnames, name, names(color_list[[name]]), NA)
}
legend("center",
       pch = 15,
       cex = 1.5,
       col = c(legendcols, rep(NA, 1), rev(col)),
       legend = c(legendnames, "Cell type", rev(names(col))),
)

proportional

# define variables to show in heatmaps:
variables_to_plot <- c("slide_name", "ANN1", "ANN3")

layout(matrix(c(1, 2, 3, 3), nrow = 2),
       widths = c(10, 3, 10, 3),
       heights = c(1, 8, 10),
      )

par(mar = c(0, 8.2, 0, 0.2))
plot(p2$tree_col, labels = F, main = "", ylab = "", yaxt = "n")
par(mar = c(15, 8, 0, 0))

# data to plot:
mat <- t(res$prop_of_nontumor)[, p2$tree_col$order]
  mat <- replace(mat, is.na(mat), 0)
  # infer scale of negative y-axis for annotation colorbars
  ymin <- -0.15

# draw barplot:
bp <- barplot(mat,
              cex.lab = 1.5,
              col = col, border = NA,
              cex.names = 1.1,
              las = 2, main = "", ylab = "Proportion of fitted cells",
              ylim = c(ymin, max(colSums(mat)))
)

# add color bars for annotations
for (name in rev(variables_to_plot)) {
  yrange <- seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 1)[match(name, variables_to_plot) + c(0, 1)]
  xwidth <- (bp[2] - bp[1]) / 2
  for (i in 1:ncol(mat)) {
    rect(bp[i] - xwidth, yrange[2], bp[i] + xwidth, yrange[1],
         # border = NA, col = ann_colors[[name]][segmentAnnotations[match(colnames(mat)[i], segmentAnnotations$segmentID), name]]
         #border = NA, col = ann_colors[[name]][ann[p2$tree_col$order[i], name]]
         border = NA, col = color_list[[name]][tmpann[colnames(mat)[i], name]]
    )
  }
}
axis(2,
     at = seq(ymin / 3, ymin, length.out = length(variables_to_plot) + 2)[-c(1, length(variables_to_plot) + 2)],
     las = 2, labels = variables_to_plot, lty = 0, cex.axis = 1.2
)


#draw a legend:
par(mar = c(0.1, 0.1, 0.1, 0.1))
frame()
legendcols <- legendnames <- c()
#for (name in rev(names(ann_colors))) {
for (name in c("slide_name", "ANN1", "ANN3")) {
  legendcols <- c(legendcols, NA, color_list[[name]], NA)
  legendnames <- c(legendnames, name, names(color_list[[name]]), NA)
}
legend("center",
       pch = 15,
       cex = 1.4,
       col = c(legendcols, rep(NA, 1), rev(col)),
       legend = c(legendnames, "Cell type", rev(names(col))),
)

Boxplots

Boxplot per cell from the spatial deconvolution. Choose your annotation in which you want to compare (ANN). The annotation will be divided in conditions and each cell with have a boxplot which include the abundance score of each condition. A statistical test has been added inside the boxplot to check for significance. Feel free to chance the test to fit the data.

significance code p-value *** [0, 0.001] ** (0.001, 0.01] * (0.01, 0.05] . (0.05, 0.1] (0.1, 1]

ANN <- "ANN1" # ANN column with conditions
if (length(unique(target_Data[[ANN]])) > 2) {
  test <- "kruskal.test"
} else {
  test <- "wilcox.test"
}

res_filter <- res[!res@phenoData@data[["beta"]]==0]
res_filter <- as.data.frame(res_filter$beta)
res_filter[res_filter == 0] <- NA
boxplot_prep <- res_filter[ , colSums(is.na(res_filter)) < nrow(res_filter)] 

boxplot_prep$condition <- rownames(boxplot_prep)

for (value in unique(target_Data[[ANN]])) {
  con <- rownames(pData(target_Data)[pData(target_Data)[[ANN]] == value,])
  boxplot_prep$condition[boxplot_prep$condition %in% con] <- value
}

boxplot <- melt(boxplot_prep)
## Using condition as id variables
# function for number of observations 
give.n <- function(x){
  return(data.frame(y = -1, label = paste0("n = ",length(x))))
 #return(c(y = median(x)*1.05, label = length(x))
# experiment with the multiplier to find the perfect position
}

ggplot(boxplot, aes(factor(condition), value, fill = condition)) + 
  geom_boxplot() + 
  geom_jitter(position=position_jitter(w=0.1,h=0.1)) +
  facet_wrap(~variable, scale="free", ncol = 5) +
  theme(text = element_text(size = 20)) +
  rotate_x_text(angle = 45) +
  labs(
    title = "Spatial Deconvolution",
    x = "Conditions",
    y = "Abuncance score") + 
  stat_compare_means(method = test, label.y = -3, size=5) + 
  stat_compare_means(label = "p.signif", method = "t.test",
                     ref.group = ".all.", hide.ns = TRUE, size=5) +
  stat_summary(fun.data = give.n, geom = "text", size = 5)

#stat_compare_means(size=5)

11 CNV

Copy Number Variation analysis with the R software package inferCNV (https://github.com/broadinstitute/inferCNV/wiki).

InferCNV is used to explore tumor single cell RNA-Seq data to identify evidence for somatic large-scale chromosomal copy number alterations, such as gains or deletions of entire chromosomes or large segments of chromosomes. This is done by exploring expression intensity of genes across positions of tumor genome in comparison to a set of reference ‘normal’ cells. A heatmap is generated illustrating the relative expression intensities across each chromosome, and it often becomes readily apparent as to which regions of the tumor genome are over-abundant or less-abundant as compared to that of normal cells.

Per patient a cnv analysis with a provided reference group. The gene order is made from the projects .pkc file.

Select the annotation where the CNV analysis should look at specifically as group and specify a subgroup as group_filter if the group of interest is a subgroup inside the annotation. For example if the CNV analysis needs to be only on tumor regions the group would be ANNX(ANN column with the info about the tumor regions) and group_filter PanCK+. The annotation that references the patients as patients. If there are no patients leave the parameter empty as ““. The annotation that should be included in the results including a reference set as cnv_target. This will create patient specific files with all the information inferCNV needs.

Select the reference set in reference. This can be more than one. Every patient without its own reference will not be analysed.

#########PARAMETERS########
reference <- c("normal")
###########################

failed <- c()
cnv_list <- list()

for (patient in patient_list) {
  # Name output folder
  out_dir <- paste0(patient, "_CNV")
  if (str_detect(paste(readLines(paste0(patient, ".Annotations.tsv")), collapse = ''), reference) == FALSE) {
    failed <- c(failed, patient)
    next
    }

  # Create the infercnv object
  infercnv_obj = CreateInfercnvObject(raw_counts_matrix= paste0(patient, ".Counts.tsv"),
                                      annotations_file= paste0(patient, ".Annotations.tsv"),
                                      delim="\t",
                                      gene_order_file= "gene_order_Hs_WTA_v1_pkc.txt",
                                      #gene_order_file= "gencode_v19_gene_pos.txt",
                                      ref_group_names= reference, # input the normal/reference group names
                                      chr_exclude = c("chrM"))#c("chrM")) # Default excludes chrX, chrY and chrM. By only picking chrM you include the X and Y chromosomes.

  # perform infercnv operations to reveal cnv signal. For all options: https://rdrr.io/github/broadinstitute/infercnv/man/run.html
  #cnv_list[[patient]]
  infercnv_obj <- infercnv::run(infercnv_obj,
                               cutoff=0.1,  # use 1 for smart-seq, 0.1 for 10x-genomics
                               out_dir= out_dir,  # dir is auto-created for storing outputs
                               cluster_by_groups=FALSE,   # cluster
                               denoise=TRUE,
                               HMM=FALSE,
                               tumor_subcluster_partition_method = c("qnorm"),
                               analysis_mode = "subclusters",
                               no_plot=TRUE
                               #,debug=TRUE
                               )

  plot_cnv(infercnv_obj,
          out_dir = paste0(patient, "_CNV"),
          title = "inferCNV",
          obs_title = "Observations (Cells)",
          ref_title = "References (Cells)",
          cluster_by_groups = FALSE,
          cluster_references = FALSE,
          plot_chr_scale = FALSE,
          #chr_lengths = NULL,
          k_obs_groups = 1,
          contig_cex = 1.5,
          #x.center = mean(infercnv_obj@expr.data),
          x.range = "auto",
          #hclust_method = "ward.D",
          output_filename = "infercnv",
          output_format = "png",
          png_res = 300
          )
}
for (patient in patient_list) {
  print(paste(group_filter, patient))
  if (patient %in% failed) {
    print(" ^    Patient contained no reference group")
    next
  }
  img <- readPNG(paste(paste0(patient, "_CNV"), "/infercnv.png", sep = ""))
  grid::grid.newpage()
  grid::grid.raster(img)
}
## [1] " dummy"

11.1 Dendrogram

Closer look at the dendrogram from the CNV analysis per patient where the nodes as written numbers. Use the numbers to select subgroups for further analysis in ‘t-test on two subgroups’.

#out_dir <- "T1_NANO_012_CNV"
trees <- list()

for (patient in patient_list) {
  if (patient %in% failed) {
    #print("Patient contained no reference group")
    next
  }
tree <- read.tree(paste(patient,"_CNV/infercnv.observations_dendrogram.txt", sep = ""))
obv <- read.csv(paste(patient,"_CNV/infercnv.observation_groupings.txt", sep = ""), sep="")
trees[[patient]] <- ggtree(
  tree, ladderize=F) +
  geom_treescale() +
  geom_tiplab(color=obv$Annotation.Color, hjust=-.2) +
  coord_cartesian(clip = 'off') +
  theme_tree2(plot.margin=margin(6, 200, 6, 6)) +
  geom_text2(aes(label=node), hjust=-.3, size = 3) +
  ggplot2::labs(title = patient)
}

grid.arrange(grobs=trees,ncol=3)

t-test chr with groups

Compare the contrast within a chromosome. Select a chromosome, contrast, and patient of interest to run a t-test on.

#########PARAMETERS########
chr <- "chrX" # Select chromosome of interest
contrast <- c("normal", "DKD") # Select contrast
patient <- "dummy"
###########################

positions <- as.data.frame(positions)
colnames(positions) <- c("gene", "chr", "begin", "end")
select_genes <- positions$gene[positions$chr == chr] # Grab chr specific genes

annotation <- as.data.frame(read.delim(paste0(patient, ".Annotations.tsv"), header=FALSE))
colnames(annotation) <- c("Sample_ID", "ANN")
select_samples <- annotation

filter_counts <- as.data.frame(target_Data@assayData[["log_q"]])
colnames(filter_counts) <- gsub('.dcc','', colnames(filter_counts))
filter_counts <- filter_counts[,select_samples$Sample_ID] # filter out samples that are not the interesting region

select_genes <- select_genes[select_genes %in% rownames(filter_counts)] # Only use genes that are actually in the data (pkc gene file has all of them)
filter_counts <- filter_counts[select_genes,] # filter out non chromosome specific genes
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BH"
counter=1

log_q_filter <-as.data.frame(filter_counts)

comps_df<-data.frame(comp='',val='')

for (active_group1 in contrast) {
    for (active_group2 in contrast) {

      #supress reduncant compares
      if(active_group1==active_group2) {next}
      comp<-paste(sort(c(active_group1,active_group2)),collapse = "_")
      #print(comp)
      if(comp %in% comps_df$comp) {next}
      temp_df<-data.frame(comp=comp ,val=1)
      comps_df<-rbind(comps_df,temp_df)

      labels[[counter]]<-paste(active_group1," vs ", active_group2)
      group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group1]]
      group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group2]]

      #run t_tests
      results<-as.data.frame ( apply(log_q_filter, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
      colnames(results)<-"raw_p_value"

      #multiple_testing_correction
      adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
      results<-cbind(results,adj_p_value)

      #calc_fdr
      FDR<- p.adjust(results$raw_p_value,method="fdr")
      results<-cbind(results,FDR)

      #fold_changes
      #as base data is already log transformed, means need to be subtracted to get FC in log space
      fchanges<-as.data.frame( apply(log_q_filter, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]) ) ) )
      colnames(fchanges)<-"FC"
      results<-cbind(results,fchanges)

      #add genenames
      results$Gene<-rownames(results)

      #set categories based on P-value & FDR for plotting
      results$Color <- "NS or FC < 0.5"
      results$Color[results$adj_p_value < 0.05] <- "P < 0.05"
      results$Color[results$FDR < 0.05] <- "FDR < 0.05"
      results$Color[results$FDR < 0.001] <- "FDR < 0.001"
      results$Color[abs(results$FC) < 0.5] <- "NS or FC < 0.5"
      results$Color <- factor(results$Color,
                              levels = c("NS or FC < 0.5", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))

      #vulcanoplot

      # pick top genes for either side of volcano to label
      # order genes for convenience:

      results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
      top_g <- c()
      top_g <- c(top_g,
                 results[ind, 'Gene'][
                   order(results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
                 results[ind, 'Gene'][order(results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
      top_g<- unique(top_g)
      results <- results[, -1*ncol(results)] # remove invert_P from matrix

      # Graph results
      plots[[counter]]<- ggplot(results,
                                      aes(x = FC, y = -log10(adj_p_value),
                                          color = Color, label = Gene)) +
        geom_vline(xintercept = c(0.5, -0.5), lty = "dashed") +
        geom_hline(yintercept = -log10(0.05), lty = "dashed") +
        geom_point() +
        labs(x = paste("Enriched genes in", chr, "-", active_group2," <- log2(FC) -> Enriched in", active_group1),
             y = "Significance, -log10(P)",
             color = "Significance") +
        scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                      `FDR < 0.05` = "lightblue",
                                      `P < 0.05` = "orange2",
                                      `NS or FC < 0.5` = "gray"),
                           guide = guide_legend(override.aes = list(size = 4))) +
        scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
        geom_text_repel(data = subset(results, FDR<0.05 & (-0.5>FC| FC>0.5)),
                        point.padding = 0.15, color = "black", size=3.5,
                        min.segment.length = .1, box.padding = .2, lwd = 2,
                        max.overlaps = 50) +
        theme_bw(base_size = 20) +
        theme(legend.position = "bottom") +
        #ggtitle(paste(slide,": ", test, mtc,"multitest corr"))
        ggtitle(paste(patient, ": ", test, mtc,"multitest corr"))

      #store tables for display later
      tables[[counter]]<-results

      counter = counter+1
      #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
    }
  }

grid.arrange(grobs=plots,ncol=2)

t-test on two subgroups

Select nodes from the dendrogram as subgroups, in this case nodes 18 and 15. Decide on the involved chromosomes and add them into the chr_list. Specify the patient in patient.

#########PARAMETERS########
chr_list <- c("chr22") # Choose needed chromosomes of the target area. Think of it as the x-axis
patient <- "dummy"
node_interest <- 233 # Choose subgroup 1
node_interest2 <- 216 # Choose subgroup 2
###########################

tree <- read.tree(paste(patient,"_CNV/infercnv.observations_dendrogram.txt", sep = ""))
tree_info <- tree %>% as.treedata %>% as_tibble # transform tree into accessible data

samples_interest <- offspring(tree_info, node_interest) # Get labels attached to the group
samples_interest <- as.character(na.omit(samples_interest$label)) # formatting
paste("Subgroup 1: ", samples_interest)
##  [1] "Subgroup 1:  DSP-1001250007864-D-H08"
##  [2] "Subgroup 1:  DSP-1001250007864-D-H10"
##  [3] "Subgroup 1:  DSP-1001250007864-D-H06"
##  [4] "Subgroup 1:  DSP-1001250007864-D-H02"
##  [5] "Subgroup 1:  DSP-1001250007864-D-H04"
##  [6] "Subgroup 1:  DSP-1001250007864-D-H11"
##  [7] "Subgroup 1:  DSP-1001250007864-D-H12"
##  [8] "Subgroup 1:  DSP-1001250007864-D-H05"
##  [9] "Subgroup 1:  DSP-1001250007864-D-H07"
## [10] "Subgroup 1:  DSP-1001250007864-D-H09"
## [11] "Subgroup 1:  DSP-1001250007864-D-H01"
## [12] "Subgroup 1:  DSP-1001250007864-D-H03"
## [13] "Subgroup 1:  DSP-1001250007851-H-A09"
## [14] "Subgroup 1:  DSP-1001250007851-H-C04"
## [15] "Subgroup 1:  DSP-1001250007851-H-D06"
## [16] "Subgroup 1:  DSP-1001250007851-H-C07"
## [17] "Subgroup 1:  DSP-1001250007851-H-D12"
## [18] "Subgroup 1:  DSP-1001250007851-H-A12"
## [19] "Subgroup 1:  DSP-1001250007851-H-A03"
## [20] "Subgroup 1:  DSP-1001250007851-H-D09"
## [21] "Subgroup 1:  DSP-1001250007851-H-A04"
## [22] "Subgroup 1:  DSP-1001250007851-H-D10"
## [23] "Subgroup 1:  DSP-1001250007851-H-A10"
## [24] "Subgroup 1:  DSP-1001250007851-H-C01"
## [25] "Subgroup 1:  DSP-1001250007851-H-C03"
## [26] "Subgroup 1:  DSP-1001250007851-H-A05"
## [27] "Subgroup 1:  DSP-1001250007851-H-C02"
## [28] "Subgroup 1:  DSP-1001250007851-H-B12"
## [29] "Subgroup 1:  DSP-1001250007851-H-B06"
## [30] "Subgroup 1:  DSP-1001250007851-H-B11"
## [31] "Subgroup 1:  DSP-1001250007851-H-B04"
## [32] "Subgroup 1:  DSP-1001250007851-H-B05"
## [33] "Subgroup 1:  DSP-1001250007851-H-D11"
## [34] "Subgroup 1:  DSP-1001250007851-H-D05"
## [35] "Subgroup 1:  DSP-1001250007851-H-B07"
## [36] "Subgroup 1:  DSP-1001250007851-H-C08"
## [37] "Subgroup 1:  DSP-1001250007851-H-D08"
## [38] "Subgroup 1:  DSP-1001250007851-H-B10"
## [39] "Subgroup 1:  DSP-1001250007851-H-A02"
## [40] "Subgroup 1:  DSP-1001250007851-H-A08"
## [41] "Subgroup 1:  DSP-1001250007851-H-A06"
## [42] "Subgroup 1:  DSP-1001250007851-H-A11"
## [43] "Subgroup 1:  DSP-1001250007851-H-D04"
## [44] "Subgroup 1:  DSP-1001250007851-H-D07"
## [45] "Subgroup 1:  DSP-1001250007851-H-B01"
## [46] "Subgroup 1:  DSP-1001250007851-H-B08"
## [47] "Subgroup 1:  DSP-1001250007851-H-A07"
## [48] "Subgroup 1:  DSP-1001250007851-H-B02"
## [49] "Subgroup 1:  DSP-1001250007851-H-B03"
## [50] "Subgroup 1:  DSP-1001250007851-H-C06"
## [51] "Subgroup 1:  DSP-1001250007851-H-D01"
## [52] "Subgroup 1:  DSP-1001250007851-H-C12"
## [53] "Subgroup 1:  DSP-1001250007851-H-C09"
## [54] "Subgroup 1:  DSP-1001250007851-H-C05"
## [55] "Subgroup 1:  DSP-1001250007851-H-C11"
## [56] "Subgroup 1:  DSP-1001250007851-H-C10"
## [57] "Subgroup 1:  DSP-1001250007851-H-B09"
## [58] "Subgroup 1:  DSP-1001250007851-H-D03"
## [59] "Subgroup 1:  DSP-1001250007851-H-D02"
## [60] "Subgroup 1:  DSP-1001250007868-B-A02"
samples_interest2 <- offspring(tree_info, node_interest2)
samples_interest2 <- as.character(na.omit(samples_interest2$label))
paste("Subgroup 2: ", samples_interest2)
##  [1] "Subgroup 2:  DSP-1001250007851-H-F10"
##  [2] "Subgroup 2:  DSP-1001250007851-H-F04"
##  [3] "Subgroup 2:  DSP-1001250007851-H-F11"
##  [4] "Subgroup 2:  DSP-1001250007851-H-F01"
##  [5] "Subgroup 2:  DSP-1001250007851-H-F05"
##  [6] "Subgroup 2:  DSP-1001250007851-H-E08"
##  [7] "Subgroup 2:  DSP-1001250007851-H-F03"
##  [8] "Subgroup 2:  DSP-1001250007851-H-F02"
##  [9] "Subgroup 2:  DSP-1001250007851-H-F09"
## [10] "Subgroup 2:  DSP-1001250007851-H-F06"
## [11] "Subgroup 2:  DSP-1001250007851-H-F07"
## [12] "Subgroup 2:  DSP-1001250007868-B-A05"
## [13] "Subgroup 2:  DSP-1001250007868-B-A03"
## [14] "Subgroup 2:  DSP-1001250007868-B-B01"
## [15] "Subgroup 2:  DSP-1001250007851-H-F12"
## [16] "Subgroup 2:  DSP-1001250007868-B-A04"
## [17] "Subgroup 2:  DSP-1002510007866-C-G02"
## [18] "Subgroup 2:  DSP-1002510007866-C-H12"
positions <- as.data.frame(positions)
colnames(positions) <- c("gene", "chr", "begin", "end") # Formatting

select_genes <- positions$gene[positions$chr %in% chr_list] # | positions$chr == chr2] # Grab chromosome specific genes

annotation <- as.data.frame(read.delim(paste0(patient, ".Annotations.tsv"), header=FALSE))
colnames(annotation) <- c("Sample_ID", "ANN")
select_samples <- annotation[annotation$Sample_ID %in% samples_interest | annotation$Sample_ID %in% samples_interest2,] # Grab subgroup specific Sample IDs

filter_counts <- target_Data@assayData[["log_q"]]
colnames(filter_counts) <- gsub('.dcc','', colnames(filter_counts))
filter_counts <- filter_counts[,select_samples$Sample_ID] # Filter Sample ID's

select_genes <- select_genes[select_genes %in% rownames(filter_counts)] # Only use genes that are actually in the data (pkc gene file has all of them)
filter_counts <- filter_counts[select_genes,] # Filter genes
plots<-list()
tables<-list()
labels<-list()
test<-"ttest"
mtc<-"BH"
counter=1

log_q_filter <-as.data.frame(filter_counts)

comps_df<-data.frame(comp='',val='')


active_group1 <- samples_interest #subgroup 1
active_group2 <- samples_interest2 #subgroup 2

# for (active_group1 in c("sub1")) {
#     for (active_group2 in c("sub2")) {

      #supress reduncant compares
      #if(active_group1==active_group2) {next}
      #comp<-paste(sort(c(active_group1,active_group2)),collapse = "_")
      #print(comp)
      #if(comp %in% comps_df$comp) {next}
      temp_df<-data.frame(comp=comp ,val=1)
      comps_df<-rbind(comps_df,temp_df)

      # labels[[counter]]<-paste(active_group1," vs ", active_group2)
      # group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group1]]
      # group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$ANN==active_group2]]

      labels[[counter]]<-paste(active_group1," vs ", active_group2)
      group1<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$Sample_ID %in% active_group1]]
      group2<-log_q_filter[,names(as.data.frame(filter_counts))[select_samples$Sample_ID %in% active_group2]]

      #run t_tests
      results<-as.data.frame ( apply(log_q_filter, 1, function(x) t.test(x[colnames(group1)],x[colnames(group2)])$p.value) )
      colnames(results)<-"raw_p_value"

      #multiple_testing_correction
      adj_p_value<- p.adjust(results$raw_p_value,method=mtc)
      results<-cbind(results,adj_p_value)

      #calc_fdr
      FDR<- p.adjust(results$raw_p_value,method="fdr")
      results<-cbind(results,FDR)

      #fold_changes
      #as base data is already log transformed, means need to be subtracted to get FC in log space
      fchanges<-as.data.frame( apply(log_q_filter, 1, function(x) (mean(x[colnames(group1)]) - mean(x[colnames(group2)]) ) ) )
      colnames(fchanges)<-"FC"
      results<-cbind(results,fchanges)

      #add genenames
      results$Gene<-rownames(results)

      #set categories based on P-value & FDR for plotting
      results$Color <- "NS or FC < 0.5"
      results$Color[results$adj_p_value < 0.05] <- "P < 0.05"
      results$Color[results$FDR < 0.05] <- "FDR < 0.05"
      results$Color[results$FDR < 0.001] <- "FDR < 0.001"
      results$Color[abs(results$FC) < 0.5] <- "NS or FC < 0.5"
      results$Color <- factor(results$Color,
                              levels = c("NS or FC < 0.5", "P < 0.05", "FDR < 0.05", "FDR < 0.001"))

      #vulcanoplot

      # pick top genes for either side of volcano to label
      # order genes for convenience:

      results$invert_P <- (-log10(results$adj_p_value)) * sign(results$FC)
      top_g <- c()
      top_g <- c(top_g,
                 results[ind, 'Gene'][
                   order(results[ind, 'invert_P'], decreasing = TRUE)[1:15]],
                 results[ind, 'Gene'][order(results[ind, 'invert_P'], decreasing = FALSE)[1:15]])
      top_g<- unique(top_g)
      results <- results[, -1*ncol(results)] # remove invert_P from matrix

      # Graph results
      #plots[[counter]]<- ggplot(results,
      p <- ggplot(results,
                                      aes(x = FC, y = -log10(adj_p_value),
                                          color = Color, label = Gene)) +
        geom_vline(xintercept = c(0.5, -0.5), lty = "dashed") +
        geom_hline(yintercept = -log10(0.05), lty = "dashed") +
        geom_point() +
        labs(x = paste("Enriched genes from", chr_list, "in", "subgroup 2"," <- log2(FC) -> Enriched in", "subgroup 1"),
             y = "Significance, -log10(P)",
             color = "Significance") +
        scale_color_manual(values = c(`FDR < 0.001` = "dodgerblue",
                                      `FDR < 0.05` = "lightblue",
                                      `P < 0.05` = "orange2",
                                      `NS or FC < 0.5` = "gray"),
                           guide = guide_legend(override.aes = list(size = 4))) +
        scale_y_continuous(expand = expansion(mult = c(0,0.05))) +
        geom_text_repel(data = subset(results, FDR<0.05 & (-0.5>FC| FC>0.5)),
                        point.padding = 0.15, color = "black", size=3.5,
                        min.segment.length = .1, box.padding = .2, lwd = 2,
                        max.overlaps = 50) +
        theme_bw(base_size = 20) +
        theme(legend.position = "bottom") +
        #ggtitle(paste(slide,": ", test, mtc,"multitest corr"))
        ggtitle(paste(patient, ": ", test, mtc,"multitest corr"))

      #store tables for display later
      tables[[counter]]<-results

      counter = counter+1
      #datatable(subset(results, Gene %in% GOI), rownames=FALSE,caption = paste("DE results ", active_group1," vs ", active_group2))
  #   }
  # }

#ggplotly(p)
p

#grid.arrange(grobs=plots,ncol=2)

12 Code & Versions

Pipelineversion: v1 based on: https://bioconductor.org/packages/devel/workflows/vignettes/GeoMxWorkflows/inst/doc/GeomxTools_RNA-NGS_Analysis.html

The underlying code can be downloaded from the ‘Code’, button on the top of this page. Choose option ‘download Rmd’ to download the full pipeline which can be opened in R or Rstudio. Some filepaths are hardcoded and need to be changed according to your setup.

12.1 R session information

sessionInfo()
## R version 4.2.1 (2022-06-23 ucrt)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19044)
## 
## Matrix products: default
## 
## locale:
## [1] LC_COLLATE=English_Netherlands.utf8  LC_CTYPE=English_Netherlands.utf8   
## [3] LC_MONETARY=English_Netherlands.utf8 LC_NUMERIC=C                        
## [5] LC_TIME=English_Netherlands.utf8    
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] ggpubr_0.6.0                tidytree_0.4.2             
##  [3] ggtree_3.6.2                Biostrings_2.66.0          
##  [5] XVector_0.38.0              ape_5.6-2                  
##  [7] infercnv_1.14.0             preprocessCore_1.60.1      
##  [9] sva_3.46.0                  BiocParallel_1.32.1        
## [11] genefilter_1.80.1           mgcv_1.8-40                
## [13] nlme_3.1-157                scater_1.26.1              
## [15] scuttle_1.8.1               ggalluvial_0.12.3          
## [17] limma_3.54.0                SpatialExperiment_1.8.0    
## [19] SingleCellExperiment_1.20.0 SummarizedExperiment_1.28.0
## [21] GenomicRanges_1.50.1        GenomeInfoDb_1.34.3        
## [23] IRanges_2.32.0              MatrixGenerics_1.10.0      
## [25] matrixStats_0.63.0          standR_1.3.1               
## [27] viridis_0.6.2               viridisLite_0.4.1          
## [29] readxl_1.4.1                png_0.1-7                  
## [31] pathview_1.38.0             fgsea_1.24.0               
## [33] clusterProfiler_4.6.2       lubridate_1.9.2            
## [35] forcats_1.0.0               stringr_1.5.0              
## [37] purrr_1.0.1                 readr_2.1.4                
## [39] tidyr_1.3.0                 tibble_3.1.8               
## [41] tidyverse_2.0.0             gt_0.8.0                   
## [43] SpatialOmicsOverlay_0.99.11 RColorBrewer_1.1-3         
## [45] gridExtra_2.3               plotly_4.10.1              
## [47] DT_0.26                     ggrepel_0.9.2              
## [49] pheatmap_1.0.12             Rtsne_0.16                 
## [51] umap_0.2.9.0                cowplot_1.1.1              
## [53] reshape2_1.4.4              scales_1.2.1               
## [55] ggforce_0.4.1               dplyr_1.1.0                
## [57] knitr_1.41                  msigdbr_7.5.1              
## [59] GSVA_1.46.0                 SpatialDecon_1.8.0         
## [61] GeoMxWorkflows_1.5.0        GeomxTools_3.2.0           
## [63] NanoStringNCTools_1.6.0     ggplot2_3.4.1              
## [65] S4Vectors_0.36.0            Biobase_2.58.0             
## [67] BiocGenerics_0.44.0        
## 
## loaded via a namespace (and not attached):
##   [1] graphlayouts_0.8.3        pbapply_1.6-0            
##   [3] lattice_0.20-45           rJava_1.0-6              
##   [5] vctrs_0.6.0               blob_1.2.3               
##   [7] survival_3.3-1            spatstat.data_3.0-0      
##   [9] later_1.3.0               nloptr_2.0.3             
##  [11] DBI_1.1.3                 R.utils_2.12.2           
##  [13] rappdirs_0.3.3            uwot_0.1.14              
##  [15] dqrng_0.3.0               jpeg_0.1-9               
##  [17] zlibbioc_1.44.0           htmlwidgets_1.5.4        
##  [19] mvtnorm_1.1-3             future_1.29.0            
##  [21] leiden_0.4.3              logNormReg_0.5-0         
##  [23] parallel_4.2.1            irlba_2.3.5.1            
##  [25] tidygraph_1.2.2           Rcpp_1.0.9               
##  [27] KernSmooth_2.23-20        promises_1.2.0.1         
##  [29] DelayedArray_0.24.0       RcppParallel_5.1.5       
##  [31] magick_2.7.3              graph_1.76.0             
##  [33] RSpectra_0.16-1           fastmatch_1.1-3          
##  [35] rjags_4-13                digest_0.6.30            
##  [37] sctransform_0.3.5         scatterpie_0.1.8         
##  [39] DOSE_3.24.2               ggraph_2.1.0             
##  [41] pkgconfig_2.0.3           GO.db_3.16.0             
##  [43] spatstat.random_3.0-1     DelayedMatrixStats_1.20.0
##  [45] ggbeeswarm_0.6.0          iterators_1.0.14         
##  [47] minqa_1.2.5               DropletUtils_1.18.0      
##  [49] reticulate_1.26           beeswarm_0.4.0           
##  [51] modeltools_0.2-23         xfun_0.35                
##  [53] bslib_0.4.1               zoo_1.8-11               
##  [55] tidyselect_1.2.0          ica_1.0-3                
##  [57] gson_0.1.0                snow_0.4-4               
##  [59] rlang_1.1.0               jquerylib_0.1.4          
##  [61] glue_1.6.2                EBImage_4.40.0           
##  [63] lambda.r_1.2.4            ggsignif_0.6.4           
##  [65] labeling_0.4.2            GGally_2.1.2             
##  [67] httpuv_1.6.6              repmis_0.5               
##  [69] BiocNeighbors_1.16.0      TH.data_1.1-1            
##  [71] annotate_1.76.0           jsonlite_1.8.3           
##  [73] bit_4.0.5                 mime_0.12                
##  [75] systemfonts_1.0.4         gplots_3.1.3             
##  [77] BiocStyle_2.26.0          stringi_1.7.8            
##  [79] spatstat.sparse_3.0-0     scattermore_0.8          
##  [81] spatstat.explore_3.0-5    yulab.utils_0.0.6        
##  [83] bitops_1.0-7              cli_3.4.1                
##  [85] rhdf5filters_1.10.0       RSQLite_2.2.18           
##  [87] libcoin_1.0-9             data.table_1.14.6        
##  [89] KEGGgraph_1.58.0          timechange_0.1.1         
##  [91] rstudioapi_0.14           fftwtools_0.9-11         
##  [93] qvalue_2.30.0             fastcluster_1.2.3        
##  [95] locfit_1.5-9.6            listenv_0.8.0            
##  [97] ggthemes_4.2.4            miniUI_0.1.1.1           
##  [99] gridGraphics_0.5-1        R.oo_1.25.0              
## [101] dbplyr_2.3.1              lifecycle_1.0.3          
## [103] munsell_0.5.0             cellranger_1.1.0         
## [105] R.methodsS3_1.8.2         caTools_1.18.2           
## [107] codetools_0.2-18          coda_0.19-4              
## [109] vipor_0.4.5               lmtest_0.9-40            
## [111] xtable_1.8-4              ROCR_1.0-11              
## [113] formatR_1.14              BiocManager_1.30.19      
## [115] abind_1.4-5               farver_2.1.1             
## [117] parallelly_1.32.1         RANN_2.6.1               
## [119] aplot_0.1.10              askpass_1.1              
## [121] tiff_0.1-11               parallelDist_0.2.6       
## [123] SeuratObject_4.1.3        RcppAnnoy_0.0.20         
## [125] goftest_1.2-3             patchwork_1.1.2          
## [127] futile.options_1.0.1      cluster_2.1.3            
## [129] future.apply_1.10.0       Seurat_4.3.0             
## [131] Matrix_1.5-3              ellipsis_0.3.2           
## [133] ggridges_0.5.4            igraph_1.3.5             
## [135] lmerTest_3.1-3            argparse_2.2.1           
## [137] spatstat.utils_3.0-1      htmltools_0.5.3          
## [139] BiocFileCache_2.6.0       yaml_2.3.6               
## [141] utf8_1.2.2                XML_3.99-0.12            
## [143] withr_2.5.0               fitdistrplus_1.1-8       
## [145] bit64_4.0.5               multcomp_1.4-20          
## [147] foreach_1.5.2             outliers_0.15            
## [149] progressr_0.11.0          GOSemSim_2.24.0          
## [151] rsvd_1.0.5                ScaledMatrix_1.6.0       
## [153] memoise_2.0.1             evaluate_0.18            
## [155] tzdb_0.3.0                curl_4.3.3               
## [157] fansi_1.0.3               highr_0.9                
## [159] ggiraph_0.8.4             GSEABase_1.60.0          
## [161] tensor_1.5                edgeR_3.40.0             
## [163] cachem_1.0.6              org.Hs.eg.db_3.16.0      
## [165] deldir_1.0-6              HDO.db_0.99.1            
## [167] babelgene_22.9            rjson_0.2.21             
## [169] rstatix_0.7.2             tools_4.2.1              
## [171] sass_0.4.3                sandwich_3.0-2           
## [173] magrittr_2.0.3            RCurl_1.98-1.9           
## [175] car_3.1-1                 phyclust_0.1-32          
## [177] ggplotify_0.1.0           xml2_1.3.3               
## [179] httr_1.4.5                RBioFormats_0.99.9       
## [181] rmarkdown_2.18            boot_1.3-28              
## [183] globals_0.16.2            R6_2.5.1                 
## [185] Rhdf5lib_1.20.0           KEGGREST_1.38.0          
## [187] treeio_1.22.0             gtools_3.9.4             
## [189] coin_1.4-2                beachmat_2.14.0          
## [191] HDF5Array_1.26.0          BiocSingular_1.14.0      
## [193] rhdf5_2.42.0              splines_4.2.1            
## [195] carData_3.0-5             ggfun_0.0.9              
## [197] colorspace_2.0-3          generics_0.1.3           
## [199] base64enc_0.1-3           gridtext_0.1.5           
## [201] pillar_1.8.1              Rgraphviz_2.42.0         
## [203] tweenr_2.0.2              sp_1.5-1                 
## [205] uuid_1.1-0                R.cache_0.16.0           
## [207] GenomeInfoDbData_1.2.9    plyr_1.8.8               
## [209] gtable_0.3.1              futile.logger_1.4.3      
## [211] shadowtext_0.1.2          fastmap_1.1.0            
## [213] EnvStats_2.7.0            crosstalk_1.2.0          
## [215] doParallel_1.0.17         AnnotationDbi_1.60.0     
## [217] broom_1.0.4               backports_1.4.1          
## [219] openssl_2.0.4             filelock_1.0.2           
## [221] plotrix_3.8-2             lme4_1.1-31              
## [223] enrichplot_1.18.3         hms_1.1.2                
## [225] shiny_1.7.3               polyclip_1.10-4          
## [227] grid_4.2.1                numDeriv_2016.8-1.1      
## [229] lazyeval_0.2.2            crayon_1.5.2             
## [231] MASS_7.3-57               downloader_0.4           
## [233] sparseMatrixStats_1.10.0  reshape_0.8.9            
## [235] compiler_4.2.1            ggtext_0.1.2             
## [237] spatstat.geom_3.0-3

12.2 References

1 Levi van Hijfte, Marjolein Geurts, Wies R. Vallentgoed, Paul H.C. Eilers, Peter A.E. Sillevis Smitt, Reno Debets, Pim J. French, Alternative normalization and analysis pipeline to address systematic bias in NanoString GeoMx Digital Spatial Profiling data, iScience, Volume 26, Issue 1, 2023, 105760, ISSN 2589-0042, https://doi.org/10.1016/j.isci.2022.105760.

2 Zhao, Y., Wong, L. & Goh, W.W.B. How to do quantile normalization correctly for gene expression data analyses. Sci Rep 10, 15534 (2020). https://doi.org/10.1038/s41598-020-72664-6

knitr::knit_exit()
LS0tDQp0aXRsZTogIk5hbm9zdHJpbmcgR2VvTXggYW5hbHlzaXMiDQphdXRob3I6ICJJZXMgTmlqbWFuICYgUGltIEtsb29zdGVybWFuIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UpDQpgYGANCg0KIVtdKGh0dHA6Ly91c2VxLm5sL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzEyL1VTRVEtbG9nby1zdWJ0aXRsZS5wbmcpDQoNCiMgS2xhbnQ6IFVzZXENCg0KIyBQcm9qZWN0OiBTcGF0aWFsIE9yZ2FuIEF0bGFzDQoNCiMgRGF0YXNldDogS2lkbmV5DQoNCioqZGF0ZTogYHIgZm9ybWF0KFN5cy50aW1lKCksICclSCAlTSAlYSAlZCAlQiwgJVknKWAqKg0KDQohW10oaHR0cDovL3VzZXEubmwvd3AtY29udGVudC91cGxvYWRzLzIwMjIvMTIvZGVjb3JhdGlvbi1zdHJva2UtZmxhdC5wbmcpDQoNCioqbG9hZGluZyBkZXBlbmRlbmNpZXMqKiBQbGVhc2UgbWFrZSBzdXJlIHRoZSBmb2xsb3dpbmcgcGFja2FnZXMgYXJlDQppbnN0YWxsZWQgYW5kIHJlcXVpcmVkIGxpYnJhcmllcyBjYW4gYmUgbG9hZGVkOg0KDQotICAgaW5zdGFsbC5wYWNrYWdlcygicGtnYnVpbGQiKSAvLyBwa2didWlsZDo6Y2hlY2tfYnVpbGRfdG9vbHMoKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikNCi0gICBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIk5hbm9zdHJpbmctQmlvc3RhdHMvTmFub1N0cmluZ05DVG9vbHMiKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiTmFub3N0cmluZy1CaW9zdGF0cy9HZW9teFRvb2xzIiwgcmVmID0NCiAgICAiZGV2IikNCi0gICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiR2VvTXhXb3JrZmxvd3MiKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiRGF2aXNMYWJvcmF0b3J5L3N0YW5kUiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIlNwYXRpYWxEZWNvbiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkdTVkEiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQotICAgaW5zdGFsbC5wYWNrYWdlcygiRFQiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoIm1zaWdkYnIiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoImRpZ2VzdCIpDQotICAgaW5zdGFsbC5wYWNrYWdlcygicm1hcmtkb3duIikNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCJrYWJsZSIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkVCSW1hZ2UiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoJ3NjYXR0ZXJtb3JlJykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdwYmFwcGx5JykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdwbG90cml4JykNCi0gICBpbnN0YWxsLnBhY2thZ2VzKCdnZ3RleHQnKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoJ1JCaW9Gb3JtYXRzJykNCi0gICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiYW9sZXMvUkJpb0Zvcm1hdHMiKQ0KLSAgIGluc3RhbGwucGFja2FnZXMoIkM6L1VzZXJzL2luaWptYW4vRG93bmxvYWRzL1NwYXRpYWxPbWljc092ZXJsYXktMC45OS4xMy1iZXRhLnRhci5neiIsDQogICAgZGVwZW5kZW5jaWVzID0gVFJVRSwgcmVwb3MgPSBOVUxMKQ0KLSAgIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiRGF2aXNMYWJvcmF0b3J5L3N0YW5kUiIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoImNsdXN0ZXJQcm9maWxlciIpDQotICAgQmlvY01hbmFnZXI6Omluc3RhbGwoInBhdGh2aWV3IikNCg0KYGBge3IgbG9hZF9saWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQoNCiNsb2FkIGxpYnJhcmllcw0KDQojc3RhY2tfc2l6ZSA8LSBnZXRPcHRpb24oInBhbmRvYy5zdGFjay5zaXplIiwgZGVmYXVsdCA9ICI1MTJtIikNCiNvcHRpb25zKGphdmEucGFyYW1ldGVycyA9IGMoIi1YWDorVXNlQ29uY01hcmtTd2VlcEdDIiwgIi1YbXg4MTkybSwgLVhYOk1ldGFzcGFjZVNpemU9MTAyNE0iKSkNCm9wdGlvbnMoamF2YS5wYXJhbWV0ZXJzID0gYygiLVhYOitVc2VDb25jTWFya1N3ZWVwR0MiLCAiLVhteDgxOTJtIikpDQpsaWJyYXJ5KE5hbm9TdHJpbmdOQ1Rvb2xzKQ0KbGlicmFyeShHZW9teFRvb2xzKQ0KbGlicmFyeShHZW9NeFdvcmtmbG93cykNCmxpYnJhcnkoU3BhdGlhbERlY29uKQ0KbGlicmFyeShHU1ZBKSAjZm9yIHBhdGh3YXkgYW5hbHlzZXMNCmxpYnJhcnkobXNpZ2RicikgI2ZvciBtb2xlY3VsYXIgc2lnbmF0dXJlcyBpbiBwYXRod2F5IGFuYWx5c2VzDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dmb3JjZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc2NhbGVzKSAjIGZvciBwZXJjZW50DQpsaWJyYXJ5KHJlc2hhcGUyKSAgIyBmb3IgbWVsdA0KbGlicmFyeShjb3dwbG90KSAgICMgZm9yIHBsb3RfZ3JpZA0KbGlicmFyeSh1bWFwKQ0KbGlicmFyeShSdHNuZSkNCmxpYnJhcnkocGhlYXRtYXApICAjIGZvciBwaGVhdG1hcA0KbGlicmFyeShnZ3JlcGVsKSANCmxpYnJhcnkoc2NhbGVzKSAjZm9yIGdncGxvdCBwZWF1ZG9sb2cgdG8gcHJldmVudCBlcnJvcnMgb24gbG9nKDApDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShwbG90bHkpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KbGlicmFyeShTcGF0aWFsT21pY3NPdmVybGF5KQ0KbGlicmFyeShndCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpDQpsaWJyYXJ5KGZnc2VhKQ0KbGlicmFyeShwYXRodmlldykNCmxpYnJhcnkocG5nKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KHZpcmlkaXMpDQoNCmxpYnJhcnkoc3RhbmRSKQ0KbGlicmFyeShTcGF0aWFsRXhwZXJpbWVudCkNCmxpYnJhcnkobGltbWEpDQpsaWJyYXJ5KGdnYWxsdXZpYWwpDQpsaWJyYXJ5KHNjYXRlcikNCg0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJzdmEiKQ0KbGlicmFyeShzdmEpDQoNCiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgicHJlcHJvY2Vzc0NvcmUiKSAjcXVhbnRpbGUgbm9ybQ0KbGlicmFyeShwcmVwcm9jZXNzQ29yZSkNCg0KI2luc3RhbGwucGFja2FnZXMoIlBvbHljaHJvbWUiKSAjR2V0IGNvbG9ycw0KI2xpYnJhcnkoUG9seWNocm9tZSkNCg0KDQpsaWJyYXJ5KGluZmVyY252KSAjIENOVg0KbGlicmFyeSgiYXBlIikNCmxpYnJhcnkoIkJpb3N0cmluZ3MiKQ0KbGlicmFyeSgiZ2d0cmVlIikNCmxpYnJhcnkodGlkeXRyZWUpDQpsaWJyYXJ5KGdncHVicikNCmBgYA0KDQojIDEgbG9hZGluZyBiYXNlIGZpbGVzDQoNCmBgYHtyIGxvYWRpbmdfYmFzZV9kYXRhfQ0KIyBSZWZlcmVuY2UgdGhlIG1haW4gZm9sZGVyICdmaWxlLnBhdGgnIGNvbnRhaW5pbmcgdGhlIHN1Yi1mb2xkZXJzIHdpdGggZWFjaCBkYXRhIGZpbGUgdHlwZToNCmRhdGFkaXI8LWZpbGUucGF0aCgiTDovcGtsb29zdGVybWFuL0dpdGh1Yi9ES0RfS2lkbmV5LyIpDQojZGF0YWRpcjwtZmlsZS5wYXRoKCJFOi9ES0RfS2lkbmV0LyIpDQpgYGANCg0KVG8gbG9jYXRlIGEgc3BlY2lmaWMgZmlsZSBwYXRoIHJlcGxhY2UgdGhlIGFib3ZlIGxpbmUgd2l0aCBkYXRhZGlyIFw8LQ0KZmlsZS5wYXRoKCJcfi9Gb2xkZXIvU3ViRm9sZGVyL0RhdGFMb2NhdGlvbiIpIHJlcGxhY2UgdGhlIEZvbGRlciwNClN1YkZvbGRlciwgRGF0YUxvY2F0aW9uIGFzIG5lZWRlZC4gVGhlIERhdGFMb2NhdGlvbiBmb2xkZXIgc2hvdWxkDQpjb250YWluIGEgZGNjcywgcGtjcywgYW5kIGFubm90YXRpb24gZm9sZGVyIHdpdGggZWFjaCBzZXQgb2YgZmlsZXMNCnByZXNlbnQgYXMgbmVlZGVkIGF1dG9tYXRpY2FsbHkgbGlzdCBmaWxlcyBpbiBlYWNoIGRpcmVjdG9yeSBmb3IgdXNlLg0KDQoqKlRha2UgY2FyZSB5b3UgaW1wb3J0IGEgY29sdW1uIHdpdGggbnVjbGVpIGNvdW50IHNlcGFyYXRlbHkgaWYgeW91DQp3YW50LioqDQoNCmBgYHtyIHBhcnNlX2ZpbGVzfQ0KRENDRmlsZXMgPC0gZGlyKGZpbGUucGF0aChkYXRhZGlyLCAiZGNjcyIpLCBwYXR0ZXJuID0gIi5kY2MkIiwNCiAgICAgICAgICAgICAgICBmdWxsLm5hbWVzID0gVFJVRSwgcmVjdXJzaXZlID0gVFJVRSkNClBLQ0ZpbGVzIDwtIGRpcihmaWxlLnBhdGgoZGF0YWRpciwgInBrY3MiKSwgcGF0dGVybiA9ICIucGtjJCIsDQogICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUpDQpTYW1wbGVBbm5vdGF0aW9uRmlsZSA8LQ0KICBkaXIoZmlsZS5wYXRoKGRhdGFkaXIsICJhbm5vdGF0aW9uIiksIHBhdHRlcm4gPSAiXltefl0iLA0KICAgICAgZnVsbC5uYW1lcyA9IFRSVUUsIHJlY3Vyc2l2ZSA9IFRSVUUpDQpgYGANCg0KIyAyIGxvYWQgZGF0YQ0KDQpgYGB7ciBsb2FkX2RhdGF9DQpEYXRhIDwtDQogIHJlYWROYW5vU3RyaW5nR2VvTXhTZXQoZGNjRmlsZXMgPSBEQ0NGaWxlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICBwa2NGaWxlcyA9IFBLQ0ZpbGVzLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YUZpbGUgPSBTYW1wbGVBbm5vdGF0aW9uRmlsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGFTaGVldCA9ICJUZW1wbGF0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm9EYXRhRGNjQ29sTmFtZSA9ICJTYW1wbGVfSUQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHByb3RvY29sRGF0YUNvbE5hbWVzID0gYygiYW9pIiwgInJvaSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVyaW1lbnREYXRhQ29sTmFtZXMgPSBjKCJwYW5lbCIpKQ0KDQojc2F2ZSBkYXRhIHRvIHByZXZlbnQgbG9hZGluZyB0aW1lIGZvciByZXRha2VzDQojc2F2ZURhdGE8LURhdGENCiNEYXRhPC1zYXZlRGF0YQ0KDQojY2hhbmdlIERhdGEgY29sdW1uIG5hbWVzIGFuZCBtYW51YWwgY29ycmVjdGlvbiBvZiBzcGVsbGluZyBlcnJvcnMNCkRhdGFAcGhlbm9EYXRhQGRhdGFbWyJzbGlkZV9uYW1lIl1dPC1EYXRhQHBoZW5vRGF0YUBkYXRhW1sic2xpZGUgbmFtZSJdXQ0KRGF0YUBwaGVub0RhdGFAZGF0YVtbInNsaWRlIG5hbWUiXV08LSAgTlVMTA0KDQoNCiMrMSByZWZlcmVuY2VzIHRoZSBzbGlkZSBuYW1lIGNvbHVtbg0KYW5uX3NpemU8LWxlbmd0aChjb2xuYW1lcyhEYXRhQHBoZW5vRGF0YUBkYXRhKVtncmVwbCgiQU5OIixjb2xuYW1lcyhEYXRhQHBoZW5vRGF0YUBkYXRhKSldKSsxIA0KYW5uX25hbWVzPC1jKGNvbG5hbWVzKERhdGFAcGhlbm9EYXRhQGRhdGEpW2dyZXBsKCJBTk4iLGNvbG5hbWVzKERhdGFAcGhlbm9EYXRhQGRhdGEpKV0sInNsaWRlX25hbWUiKQ0KDQojIEZlZWwgZnJlZSB0byBjaGFuZ2UgdGhlIG9yZGVyIG9mIHdoaWNoIGNvbG9ycyBhcmUgYXBwb2ludGVkLg0KY29sb3I8LWMoIiNBMzQ5QTQiLCAiI0ZGRkYzMyIsICIjRTcyOThBIiwgIiMwOTE4MzMiLCAiIzFCOUU3NyIsICIjRDk1RjAyIiwgIiM3NTcwQjMiLCAgIiM2NkE2MUUiLCAiI0U2QUIwMiIsICIjOEREM0M3IiwgIiM5RjAwMEYiLCAiI0JFQkFEQSIsICIjRkI4MDcyIiwgIiM4MEIxRDMiLCAiI0ZEQjQ2MiIsICIjQjNERTY5IiwgIiNGQ0NERTUiLCAiI0Q5RDlEOSIsICIjQkM4MEJEIiwgIiNDQ0VCQzUiLCAiI0ZGRUQ2RiIsICIjMzc3RUI4IiwgIiM5ODRFQTMiLCAiIzREQUY0QSIsICIjRkY3MUNFIiwgIiNGRjdGMDAiLCAiI0E2Q0VFMyIsICIjMUY3OEI0IiwgIiNCMkRGOEEiLCAiIzMzQTAyQyIsICIjRkI5QTk5IiwgIiNFMzFBMUMiLCAiI0ZEQkY2RiIsICIjQ0FCMkQ2IiwgIiM2QTNEOUEiLCAiI0ZGRkY5OSIsICIjQjE1OTI4IikNCg0KIyBVc2UgY291bnQgYW5kIGNvdW50X21heCB0byBzZXQgdGhlIHJhbmdlIG9mIGNvbG9yIG5lZWRlZCBmb3IgZWFjaCBjb2x1bW4uDQojIFdpdGggc2V0TmFtZXMoKSB0aGUgY29sb3IgY29kZSBpcyBsaW5rZWQgdG8gZWFjaCB1bmlxdWUgaW5kaXZpZHVhbCB2YWx1ZSBvZiBlYWNoIGNvbHVtbi4NCmNvdW50PTENCmNvbG9yX2xpc3QgPSBsaXN0KCkNCmZvciAoYW5uIGluIGFubl9uYW1lcykgew0KICBjb3VudF9tYXggPSBjb3VudCtsZW5ndGgodW5pcXVlKERhdGFAcGhlbm9EYXRhQGRhdGFbW2Fubl1dKSktMQ0KICBjb2xvcl9saXN0W1thbm5dXTwtc2V0TmFtZXMoY29sb3JbY291bnQ6Y291bnRfbWF4XSwgdW5pcXVlKERhdGFAcGhlbm9EYXRhQGRhdGFbW2Fubl1dKSkNCiAgY291bnQ9Y291bnRfbWF4KzENCn0NCmBgYA0KDQpgYGB7ciBjb2xvciB0YWJsZX0NCnZhbHVlX25hbWVzID0gYygpDQpmb3IgKGFubiBpbiBhbm5fbmFtZXMpIHsNCiAgdmFsdWVfbmFtZXMgPC0gYyh2YWx1ZV9uYW1lcywgdW5pcXVlKERhdGFAcGhlbm9EYXRhQGRhdGFbW2Fubl1dKSkNCn0NCg0KY29sb3JfZGYgPC0gYXMuZGF0YS5mcmFtZSh2YWx1ZV9uYW1lcykNCmNvbG9yX2RmJGNvbG9yIDwtIGNvbG9yWzA6bGVuZ3RoKHZhbHVlX25hbWVzKV0NCg0KY29sb3JfZGYgJT4lIA0KICBtdXRhdGUoY29sb3IgPSBmY3RfaW5vcmRlcihjb2xvcikpIHw+IA0KICBndCgpICU+JSANCiAgZGF0YV9jb2xvcihjb2x1bW5zID0gY29sb3IsIGNvbG9ycyA9IGFzLmNoYXJhY3Rlcihjb2xvcikpICU+JQ0KICB0YWJfb3B0aW9ucyhjb250YWluZXIuaGVpZ2h0ID0gNTAwKQ0KYGBgDQoNCmBgYHtyfQ0KcGFzdGUoIlJlYWRzIGZyb20gZm9sbG93aW5nIHJ1bnMgdXNlZDogIix1bmlxdWUocERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSRTZXFTZXRJZCkpDQpgYGANCg0KIyAzIFN0dWR5IGRlc2lnbg0KDQpgYGB7ciBhbm5vdGF0ZX0NCnBrY3MgPC0gYW5ub3RhdGlvbihEYXRhKQ0KbW9kdWxlcyA8LSBnc3ViKCIucGtjIiwgIiIsIHBrY3MpDQprYWJsZShkYXRhLmZyYW1lKFBLQ3MgPSBwa2NzLCBtb2R1bGVzID0gbW9kdWxlcykpDQpgYGANCg0KU2VsZWN0IHRoZSBhbm5vdGF0aW9ucyB3ZSB3YW50IHRvIHNob3csIHVzZSBcYFxgIHRvIHN1cnJvdW5kIGNvbHVtbg0KbmFtZXMgd2l0aCBzcGFjZXMgb3Igc3BlY2lhbCBzeW1ib2xzDQoNCmBgYHtyIHNlbGVjdF9hbm5vdGF0aW9uc30NCiNjb3VudF9tYXQgPC0gZHBseXI6OmNvdW50KHBEYXRhKERhdGEpLCBBTk4xLEFOTjIsc2xpZGVfbmFtZSkNCmNvdW50X21hdCA8LSBwRGF0YShEYXRhKSAlPiUgZHBseXI6OmNvdW50KHBEYXRhKERhdGEpW2MoYW5uX25hbWVzKV0pDQpgYGANCg0KU2ltcGxpZnkgdGhlIHNsaWRlIG5hbWVzIGlmIHJlcXVpcmVkDQoNCmBgYHtyIHNpbXBsaWZ5X25hbWVzfQ0KIyBjb3VudF9tYXQkc2xpZGVfbmFtZSA8LSBnc3ViKCJkaXNlYXNlIiwgImQiLCBnc3ViKCJub3JtYWwiLCAibiIsIGNvdW50X21hdCRzbGlkZV9uYW1lKSkNCiNjb3VudF9tYXQkcGF0aF9hbm4gPC0gZ3N1YigiaSIsICIiLCBjb3VudF9tYXQkcGF0aF9hbm4pICNjb3JyZWN0aW5nIHNwZWxsaW5nIGVycm9yDQpgYGANCg0KR2F0aGVyIHRoZSBkYXRhIGFuZCBwbG90IGluIG9yZGVyOiBjbGFzcywgc2xpZGUgbmFtZSwgcmVnaW9uLCBzZWdtZW50DQoNCmBgYHtyIGdhdGhlcl9kYXRhfQ0KdGVzdF9nciA8LSBnYXRoZXJfc2V0X2RhdGEoY291bnRfbWF0LCAxOmFsbF9vZihhbm5fc2l6ZSkpDQp0ZXN0X2dyJHggPC0gZmFjdG9yKHRlc3RfZ3IkeCwgbGFiZWxzID0gYW5uX25hbWVzKQ0KYGBgDQoNClBsb3QgU2Fua2V5DQoNCmBgYHtyIFNhbmtleV9wbG90LCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMX0NCmdncGxvdCh0ZXN0X2dyLCBoZWlnaHQgPSAxMCwgd2lkdGggPSAxMCwgYWVzKHgsIGlkID0gaWQsIHNwbGl0ID0geSwgdmFsdWUgPSBuKSkgKw0KICBnZW9tX3BhcmFsbGVsX3NldHMoYWVzKGZpbGwgPSBBTk4yKSwgYWxwaGEgPSAwLjUsIGF4aXMud2lkdGggPSAwLjEpICsNCiAgZ2VvbV9wYXJhbGxlbF9zZXRzX2F4ZXMoYXhpcy53aWR0aCA9IDAuMikgKw0KICBnZW9tX3BhcmFsbGVsX3NldHNfbGFiZWxzKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDUpICsNCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxMikgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKDApKSArIA0KICBzY2FsZV94X2Rpc2NyZXRlKGV4cGFuZCA9IGV4cGFuc2lvbigwKSkgKw0KICBsYWJzKHggPSAiIiwgeSA9ICIiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jb2xvcl9saXN0JEFOTjIpICsNCiAgYW5ub3RhdGUoZ2VvbSA9ICJzZWdtZW50IiwgeCA9IDQuMjUsIHhlbmQgPSA0LjI1LA0KICAgICAgICAgICB5ID0gMTAsIHllbmQgPSA2MSwgbHdkID0gMikgKw0KICBhbm5vdGF0ZShnZW9tID0gInRleHQiLCB4ID0gNC4xOSwgeSA9IDI1LCBhbmdsZSA9IDkwLCBzaXplID0gNSwNCiAgICAgICAgICAgaGp1c3QgPSAwLjUsIGxhYmVsID0gIjUwIHNlZ21lbnRzIikNCmBgYA0KDQojIDQgUUMgJiBQcmUtcHJvY2Vzc2luZw0KDQpTaGlmdCBjb3VudHMgdG8gb25lDQoNCmBgYHtyIHNoaWZ0X2NvdW50c30NCiNzaGlmdCBhbnkgZXhwcmVzc2lvbiBjb3VudHMgd2l0aCBhIHZhbHVlIG9mIDAgdG8gMSB0byBlbmFibGUgaW4gZG93bnN0cmVhbSB0cmFuc2Zvcm1hdGlvbnMuDQpEYXRhIDwtIHNoaWZ0Q291bnRzT25lKERhdGEsIHVzZURBTG9naWMgPSBUUlVFKQ0KYGBgDQoNCiMgNC4xIFNlZ21lbnQgUUMNCg0KV2UgZmlyc3QgYXNzZXNzIHNlcXVlbmNpbmcgcXVhbGl0eSBhbmQgYWRlcXVhdGUgdGlzc3VlIHNhbXBsaW5nIGZvcg0KZXZlcnkgUk9JL0FPSSBzZWdtZW50Lg0KDQpFdmVyeSBST0kvQU9JIHNlZ21lbnQgd2lsbCBiZSB0ZXN0ZWQgZm9yOg0KDQpSYXcgc2VxdWVuY2luZyByZWFkczogc2VnbWVudHMgd2l0aCBcPjEwMDAgcmF3IHJlYWRzIGFyZSByZW1vdmVkLiAlDQpBbGlnbmVkLCUgVHJpbW1lZCwgb3IgJSBTdGl0Y2hlZCBzZXF1ZW5jaW5nIHJlYWRzOiBzZWdtZW50cyBiZWxvdyBcfjgwJQ0KZm9yIG9uZSBvciBtb3JlIG9mIHRoZXNlIFFDIHBhcmFtZXRlcnMgYXJlIHJlbW92ZWQuICUgU2VxdWVuY2luZw0Kc2F0dXJhdGlvbiAoWzEtZGVkdXBsaWNhdGVkIHJlYWRzL2FsaWduZWQgcmVhZHNdJSk6IHNlZ21lbnRzIGJlbG93IFx+NTAlDQpyZXF1aXJlIGFkZGl0aW9uYWwgc2VxdWVuY2luZyB0byBjYXB0dXJlIGZ1bGwgc2FtcGxlIGRpdmVyc2l0eSBhbmQgYXJlDQpub3QgdHlwaWNhbGx5IGFuYWx5emVkIHVudGlsIGltcHJvdmVkLiBOZWdhdGl2ZSBDb3VudDogdGhpcyBpcyB0aGUNCmdlb21ldHJpYyBtZWFuIG9mIHRoZSBzZXZlcmFsIHVuaXF1ZSBuZWdhdGl2ZSBwcm9iZXMgaW4gdGhlIEdlb014IHBhbmVsDQp0aGF0IGRvIG5vdCB0YXJnZXQgbVJOQSBhbmQgZXN0YWJsaXNoIHRoZSBiYWNrZ3JvdW5kIGNvdW50IGxldmVsIHBlcg0Kc2VnbWVudDsgc2VnbWVudHMgd2l0aCBsb3cgbmVnYXRpdmUgY291bnRzICgxLTEwKSBhcmUgbm90IG5lY2Vzc2FyaWx5DQpyZW1vdmVkIGJ1dCBtYXkgYmUgc3R1ZGllZCBjbG9zZXIgZm9yIGxvdyBlbmRvZ2Vub3VzIGdlbmUgc2lnbmFsIGFuZC9vcg0KaW5zdWZmaWNpZW50IHRpc3N1ZSBzYW1wbGluZy4gTm8gVGVtcGxhdGUgQ29udHJvbCAoTlRDKSBjb3VudDogdmFsdWVzDQpcPjEsMDAwIGNvdWxkIGluZGljYXRlIGNvbnRhbWluYXRpb24gZm9yIHRoZSBzZWdtZW50cyBhc3NvY2lhdGVkIHdpdGgNCnRoaXMgTlRDOyBob3dldmVyLCBpbiBjYXNlcyB3aGVyZSB0aGUgTlRDIGNvdW50IGlzIGJldHdlZW4gMSwwMDAtDQoxMCwwMDAsIHRoZSBzZWdtZW50cyBtYXkgYmUgdXNlZCBpZiB0aGUgTlRDIGRhdGEgaXMgdW5pZm9ybWx5IGxvdyAoZS5nLg0KMC0yIGNvdW50cyBmb3IgYWxsIHByb2JlcykuIE51Y2xlaTogXD4xMDAgbnVjbGVpIHBlciBzZWdtZW50IGlzDQpnZW5lcmFsbHkgcmVjb21tZW5kZWQ7IGhvd2V2ZXIsIHRoaXMgY3V0b2ZmIGlzIGhpZ2hseSBzdHVkeS90aXNzdWUNCmRlcGVuZGVudCBhbmQgbWF5IG5lZWQgdG8gYmUgcmVkdWNlZDsgd2hhdCBpcyBtb3N0IGltcG9ydGFudCBpcw0KY29uc2lzdGVuY3kgaW4gdGhlIG51Y2xlaSBkaXN0cmlidXRpb24gZm9yIHNlZ21lbnRzIHdpdGhpbiB0aGUgc3R1ZHkuDQpBcmVhOiBnZW5lcmFsbHkgY29ycmVsYXRlcyB3aXRoIG51Y2xlaTsgYSBzdHJpY3QgY3V0b2ZmIGlzIG5vdCBnZW5lcmFsbHkNCmFwcGxpZWQgYmFzZWQgb24gYXJlYS4NCg0KIyA0LjEuMSBTZWxlY3QgU2VnbWVudCBRQw0KDQpGaXJzdCwgd2Ugc2VsZWN0IHRoZSBRQyBwYXJhbWV0ZXIgY3V0b2ZmcywgYWdhaW5zdCB3aGljaCBvdXIgUk9JL0FPSQ0Kc2VnbWVudHMgd2lsbCBiZSB0ZXN0ZWQgYW5kIGZsYWdnZWQgYXBwcm9wcmlhdGVseS4gV2UgaGF2ZSBzZWxlY3RlZCB0aGUNCmFwcHJvcHJpYXRlIHN0dWR5LXNwZWNpZmljIHBhcmFtZXRlcnMgZm9yIHRoaXMgc3R1ZHkuIE5vdGU6IHRoZSBkZWZhdWx0DQpRQyB2YWx1ZXMgcmVjb21tZW5kZWQgYWJvdmUgYXJlIGFkdmlzZWQgd2hlbiBzdXJ2ZXlpbmcgYSBuZXcgZGF0YXNldCBmb3INCnRoZSBmaXJzdCB0aW1lLg0KDQpEZWZhdWx0IFFDIGN1dG9mZnMgYXJlIGNvbW1lbnRlZCBpbiAoKSBhZGphY2VudCB0byB0aGUgcmVzcGVjdGl2ZQ0KcGFyYW1ldGVycyBzdHVkeS1zcGVjaWZpYyB2YWx1ZXMgd2VyZSBzZWxlY3RlZCBhZnRlciB2aXN1YWxpemluZyB0aGUgUUMNCnJlc3VsdHMgaW4gbW9yZSBkZXRhaWwgYmVsb3cNCg0KYGBge3Igc2V0X1FDX3BhcmFtc30NClFDX3BhcmFtcyA8LQ0KICBsaXN0KG1pblNlZ21lbnRSZWFkcyA9IDEwMDAsICMgTWluaW11bSBudW1iZXIgb2YgcmVhZHMgKDEwMDApDQogICAgICAgcGVyY2VudFRyaW1tZWQgPSA4MCwgICAgIyBNaW5pbXVtICUgb2YgcmVhZHMgdHJpbW1lZCAoODAlKQ0KICAgICAgIHBlcmNlbnRTdGl0Y2hlZCA9IDgwLCAgICMgTWluaW11bSAlIG9mIHJlYWRzIHN0aXRjaGVkICg4MCUpDQogICAgICAgcGVyY2VudEFsaWduZWQgPSA3NSwgICAgIyBNaW5pbXVtICUgb2YgcmVhZHMgYWxpZ25lZCAoODAlKQ0KICAgICAgIHBlcmNlbnRTYXR1cmF0aW9uID0gNTAsICMgTWluaW11bSBzZXF1ZW5jaW5nIHNhdHVyYXRpb24gKDUwJSkNCiAgICAgICBtaW5OZWdhdGl2ZUNvdW50ID0gMSwgICAjIE1pbmltdW0gbmVnYXRpdmUgY29udHJvbCBjb3VudHMgKDEwKQ0KICAgICAgIG1heE5UQ0NvdW50ID0gOTAwMCwgICAgICMgTWF4aW11bSBjb3VudHMgb2JzZXJ2ZWQgaW4gTlRDIHdlbGwgKDEwMDApDQogICAgICAgbWluTnVjbGVpID0gMjAsICAgICAgICAjIE1pbmltdW0gIyBvZiBudWNsZWkgZXN0aW1hdGVkICgxMDApDQogICAgICAgbWluQXJlYSA9IDEwMDApICAgICAgICAgIyBNaW5pbXVtIHNlZ21lbnQgYXJlYSAoNTAwMCkNCkRhdGEgPC0NCiAgc2V0U2VnbWVudFFDRmxhZ3MoRGF0YSwgcWNDdXRvZmZzID0gUUNfcGFyYW1zKSAgICAgICAgDQoNCmNhdCgicHJlLVFDIGZlYXR1cmVzOiIsIGRpbShEYXRhKVsxXSwgIlxucHJlLVFDIHNhbXBsZXM6IiwgZGltKERhdGEpWzJdKQ0KDQojVGFibGUgZm9yIGNsYXJpZmljYXRpb24NClFDcGFyYW1zX2RmIDwtIGRhdGEuZnJhbWUgKA0KICBpdGVtcyA9IGMoIm1pblNlZ21lbnRSZWFkcyIsInBlcmNlbnRUcmltbWVkIiwicGVyY2VudFN0aXRjaGVkIiwicGVyY2VudEFsaWduZWQiLCJwZXJjZW50U2F0dXJhdGlvbiIsDQogICAgICAgICAgICAibWluTmVnYXRpdmVDb3VudCIsIm1heE5UQ0NvdW50IiwibWluTnVjbGVpIiwibWluQXJlYSIpLA0KICBkZWZhdWx0cyA9IGMoMTAwMCw4MCw4MCw4MCw1MCwxMCwxMDAwLDEwMCw1MDAwKSwNCiAgYWN0dWFsID0gYyhRQ19wYXJhbXMkbWluU2VnbWVudFJlYWRzLFFDX3BhcmFtcyRwZXJjZW50VHJpbW1lZCxRQ19wYXJhbXMkcGVyY2VudFN0aXRjaGVkLFFDX3BhcmFtcyRwZXJjZW50QWxpZ25lZCxRQ19wYXJhbXMkcGVyY2VudFNhdHVyYXRpb24sUUNfcGFyYW1zJG1pbk5lZ2F0aXZlQ291bnQsUUNfcGFyYW1zJG1heE5UQ0NvdW50LFFDX3BhcmFtcyRtaW5OdWNsZWksUUNfcGFyYW1zJG1pbkFyZWEpDQopDQoNCmRhdGF0YWJsZShRQ3BhcmFtc19kZiwgcm93bmFtZXM9RkFMU0UsDQogICAgICAgICAgY2FwdGlvbiA9ICJRQyB0aHJlc2hvbGRzIiwNCiAgICAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCBvcHRpb25zID0gbGlzdCAoDQogICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICkNCikNCg0KYGBgDQoNCkNvbGxhdGUgUUMgUmVzdWx0cw0KDQpgYGB7ciBjb2xsYXRlX1FDX3Jlc3VsdHN9DQpRQ1Jlc3VsdHMgPC0gcHJvdG9jb2xEYXRhKERhdGEpW1siUUNGbGFncyJdXQ0KZmxhZ19jb2x1bW5zIDwtIGNvbG5hbWVzKFFDUmVzdWx0cykNClFDX1N1bW1hcnkgPC0gZGF0YS5mcmFtZShQYXNzID0gY29sU3VtcyghUUNSZXN1bHRzWywgZmxhZ19jb2x1bW5zXSksDQogICAgICAgICAgICAgICAgICAgICAgICAgV2FybmluZyA9IGNvbFN1bXMoUUNSZXN1bHRzWywgZmxhZ19jb2x1bW5zXSkpDQoNClFDUmVzdWx0cyRRQ1N0YXR1cyA8LSBhcHBseShRQ1Jlc3VsdHMsIDFMLCBmdW5jdGlvbih4KSB7DQogIGlmZWxzZShzdW0oeCkgPT0gMEwsICJQQVNTIiwgIldBUk5JTkciKQ0KfSkNCg0KUUNfU3VtbWFyeVsiVE9UQUwgRkxBR1MiLCBdIDwtDQogIGMoc3VtKFFDUmVzdWx0c1ssICJRQ1N0YXR1cyJdID09ICJQQVNTIiksDQogICAgc3VtKFFDUmVzdWx0c1ssICJRQ1N0YXR1cyJdID09ICJXQVJOSU5HIikpDQoNCmNvbF9ieSA8LSAiQU5OMSINCmNvbF9ieV9wbGF0ZSA8LSAiUGxhdGVfSUQiDQpgYGANCg0KIyA0LjIgR3JhcGhpY2FsIHN1bW1hcmllcyBvZiBRQyBzdGF0aXN0aWNzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNClVzZSB0aGUgdGFiLW1lbnUgdG8gbmF2aWdhdGUhDQoNCmBgYHtyIFFDX3Bsb3R0aW5nfQ0KUUNfaGlzdG9ncmFtIDwtIGZ1bmN0aW9uKGFzc2F5X2RhdGEgPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb24gPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGxfYnkgPSBOVUxMLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRociA9IE5VTEwsDQogICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVfdHJhbnMgPSBOVUxMKSB7DQogIHBsdCA8LSBnZ3Bsb3QoYXNzYXlfZGF0YSwNCiAgICAgICAgICAgICAgICBhZXNfc3RyaW5nKHggPSBwYXN0ZTAoInVubGlzdChgIiwgYW5ub3RhdGlvbiwgImApIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gZmlsbF9ieSkpICsNCiAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTApICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSB0aHIsIGx0eSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibGFjayIpICsNCiAgICB0aGVtZV9idygpICsgZ3VpZGVzKGZpbGwgPSAibm9uZSIpICsNCiAgICBmYWNldF93cmFwKGFzLmZvcm11bGEocGFzdGUoIn4iLCBmaWxsX2J5KSksIG5yb3cgPSA0KSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbG9yX2xpc3QkQU5OMSkgKw0KICAgIGxhYnMoeCA9IGFubm90YXRpb24sIHkgPSAic2VnbWVudHMsICMiLCB0aXRsZSA9IGFubm90YXRpb24pDQogIGlmKCFpcy5udWxsKHNjYWxlX3RyYW5zKSkgew0KICAgIHBsdCA8LSBwbHQgKw0KICAgICAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gc2NhbGVfdHJhbnMpDQogIH0NCiAgcGx0DQp9DQpgYGANCg0KIyMgVHJpbW1lZA0KDQolIFRyaW1tZWQgc2VxdWVuY2luZyByZWFkczogc2VnbWVudHMgYmVsb3cgdGhlIFFDIHBhcmFtZXRlcnMgYXJlIHJlbW92ZWQuDQoNCmBgYHtyfQ0KUUNfaGlzdG9ncmFtKHNEYXRhKERhdGEpLCAiVHJpbW1lZCAoJSkiLCBjb2xfYnksIFFDX3BhcmFtcyRwZXJjZW50VHJpbW1lZCkNCmBgYA0KDQojIyBTdGljaGVkICglKQ0KDQolIFN0aWNoZWQgc2VxdWVuY2luZyByZWFkczogc2VnbWVudHMgYmVsb3cgdGhlIFFDIHBhcmFtZXRlcnMgYXJlIHJlbW92ZWQuDQoNCmBgYHtyfQ0KUUNfaGlzdG9ncmFtKHNEYXRhKERhdGEpLCAiU3RpdGNoZWQgKCUpIiwgY29sX2J5LCBRQ19wYXJhbXMkcGVyY2VudFN0aXRjaGVkKQ0KYGBgDQoNCiMjIEFsaWduZWQgKCUpDQoNCiUgQWxpZ25lZCBzZXF1ZW5jaW5nIHJlYWRzOiBzZWdtZW50cyBiZWxvdyB0aGUgUUMgcGFyYW1ldGVycyBhcmUgcmVtb3ZlZC4NCg0KYGBge3J9DQpRQ19oaXN0b2dyYW0oc0RhdGEoRGF0YSksICJBbGlnbmVkICglKSIsIGNvbF9ieSxRQ19wYXJhbXMkcGVyY2VudEFsaWduZWQpDQpgYGANCg0KIyMgU2VxdWVuY2luZyBTYXR1cmF0aW9uICglKSB7LmFjdGl2ZX0NCg0KJSBTZXF1ZW5jaW5nIHNhdHVyYXRpb24gKFsxLWRlZHVwbGljYXRlZCByZWFkcy9hbGlnbmVkIHJlYWRzXSUpOiBzZWdtZW50cyBiZWxvdyB0aGUgUUMgcGFyYW1ldGVyIHJlcXVpcmUgYWRkaXRpb25hbCBzZXF1ZW5jaW5nIHRvIGNhcHR1cmUgZnVsbCBzYW1wbGUgZGl2ZXJzaXR5IGFuZCBhcmUgbm90IHR5cGljYWxseSBhbmFseXplZCB1bnRpbCBpbXByb3ZlZC4NCg0KYGBge3J9DQpRQ19oaXN0b2dyYW0oc0RhdGEoRGF0YSksICJTYXR1cmF0ZWQgKCUpIiwgY29sX2J5LCBRQ19wYXJhbXMkcGVyY2VudFNhdHVyYXRpb24pICsNCiAgbGFicyh0aXRsZSA9ICJTZXF1ZW5jaW5nIFNhdHVyYXRpb24gKCUpIiwNCiAgICAgICB4ID0gIlNlcXVlbmNpbmcgU2F0dXJhdGlvbiAoJSkiKQ0KYGBgDQoNCiMjIEFyZWENCg0KQXJlYTogZ2VuZXJhbGx5IGNvcnJlbGF0ZXMgd2l0aCBudWNsZWk7IGEgc3RyaWN0IGN1dG9mZiBpcyBub3QgZ2VuZXJhbGx5IGFwcGxpZWQgYmFzZWQgb24gYXJlYS4NCg0KYGBge3J9DQpRQ19oaXN0b2dyYW0oc0RhdGEoRGF0YSksICJhcmVhIiwgY29sX2J5LCBRQ19wYXJhbXMkbWluQXJlYSwgc2NhbGVfdHJhbnMgPSAibG9nMTAiKQ0KYGBgDQoNCiMjIE51Y2xlaSBjb3VudA0KDQpOdWNsZWk6ID4xMDAgbnVjbGVpIHBlciBzZWdtZW50IGlzIGdlbmVyYWxseSByZWNvbW1lbmRlZDsgaG93ZXZlciwgdGhpcyBjdXRvZmYgaXMgaGlnaGx5IHN0dWR5L3Rpc3N1ZSBkZXBlbmRlbnQgYW5kIG1heSBuZWVkIHRvIGJlIHJlZHVjZWQ7IHdoYXQgaXMgbW9zdCBpbXBvcnRhbnQgaXMgY29uc2lzdGVuY3kgaW4gdGhlIG51Y2xlaSBkaXN0cmlidXRpb24gZm9yIHNlZ21lbnRzIHdpdGhpbiB0aGUgc3R1ZHkuDQoNCmBgYHtyfQ0KUUNfaGlzdG9ncmFtKHNEYXRhKERhdGEpLCAibnVjbGVpIiwgY29sX2J5LCBRQ19wYXJhbXMkbWluTnVjbGVpKQ0KYGBgDQoNCiMjIER1cGxpY2F0aW9uUmF0ZQ0KDQpTaG93IHRoZSBwZXJjZW50YWdlIG9mIGR1cGxpY2F0ZWQgcmVhZHMgcGVyIHJhdyByZWFkcy4NCg0KYGBge3J9DQpnZ3Bsb3QocERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSwNCiAgICAgICBhZXMoeCA9IFBsYXRlX0lELCBmaWxsPVBsYXRlX0lELA0KICAgICAgICAgIHkgPSAoRGVkdXBsaWNhdGVkUmVhZHMvUmF3KSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gIkRlZHVwbGljYXRlZCAvIFJhdyByZWFkcyIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyMgTmVncHJvYmVzIHZzIEVuZG9nZW5vdXMNCg0KQ29tcGFyZSBOZWdwcm9iZXMgdnMgRW5kb2dlbm91cyBleHByZXNzaW9uIHRvIHNlZSBpZiB0aGUgZW5kb2dlbm91cyAocmVhbCkgcHJvYmVzIA0KYXJlIHNpZ25pZmljYW50bHkgaGlnaGVyIHRoYW4gTmVncHJvYmVzLiBUaGlzIG1lYW5zIHRoYXQgdGhlcmUgaXMgYSANCmhpZ2ggc2lnbmFsLXRvLW5vaXNlIHJhdGlvLg0KDQpgYGB7ciBwbG90X25lZ3Byb2JlX2RhdGEsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTV9DQp0bXBfdGFyZ2V0X0RhdGEgPC0gYWdncmVnYXRlQ291bnRzKERhdGEpDQoNCiNnZXQgbmVnYXRpdmUgcHJvYmUgZGF0YQ0KbmVnczwtc3Vic2V0KHRtcF90YXJnZXRfRGF0YSxDb2RlQ2xhc3M9PSJOZWdhdGl2ZSIpDQoNCnAxPC1nZ3Bsb3QocERhdGEobmVncyksDQogICAgICAgYWVzKHggPSBBTk4yLCBmaWxsID0gQU5OMiwNCiAgICAgICAgICB5ID0gYXNzYXlEYXRhRWxlbWVudChuZWdzLCBlbHQgPSAiZXhwcnMiKSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gIk5lZ2F0aXZlIHByb2JlcyBFeHByZXNzaW9uIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxLDMwMDApLCB0cmFucyA9ICJsb2cyIikgKw0KICB0aGVtZV9idygpICsgY29vcmRfZmxpcCgpDQoNCg0KIyBnZXQgZW5kb2dlbm91cyBwcm9iZSBkYXRhDQplbmQ8LXN1YnNldCh0bXBfdGFyZ2V0X0RhdGEsQ29kZUNsYXNzPT0iRW5kb2dlbm91cyIpDQoNCnAyPC1nZ3Bsb3QocERhdGEoZW5kKSwNCiAgICAgICBhZXMoeCA9IEFOTjIsIGZpbGwgPSBBTk4yLA0KICAgICAgICAgICB5ID0gY29sTWVhbnMoYXNzYXlEYXRhRWxlbWVudChlbmQsIGVsdCA9ICJleHBycyIpKSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gIkVuZG9nZW5vdXMgcHJvYmVzIEV4cHJlc3Npb24gKG1lYW4pIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxLDMwMDApLHRyYW5zID0gImxvZzIiKSArDQogIHRoZW1lX2J3KCkgKyBjb29yZF9mbGlwKCkNCg0KcGwgPC1saXN0KHAxLHAyKQ0KcGxvdF9ncmlkKHBsb3RsaXN0PXBsLCBucm93PTIsIGFsaWduPSd2JykNCg0KYGBgDQoNCiMjIE5lZ19wcm9iZSByZWFkcyBjb21wYXJlZCB0byByYXdfcmVhZHMNCg0KU2hvdyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGRlZHVwbGljYXRlZCByZWFkcyBhbmQgbmVnYXRpdmUgY29udHJvbCByZWFkcy4gSXQgaXMgZXhwZWN0ZWQgdGhhdCBkZWR1cGxpY2F0ZWQgcmVhZHMgYXJlIHN1YnN0YW50aWFsbHkgaGlnaGVyIHRoYW4gbmVnYXRpdmUgY29udHJvbCByZWFkcy4NCg0KYGBge3J9DQoNCiMgbWFrZSBiYWNrZ3JvdW5kIHRvdGFsIG5lZyBwcm9iZSBjb3VudA0KZmRhdGFfZGY8LWZEYXRhKERhdGEpDQpuZWdwcm9iZXNuYW1lczwtcm93bmFtZXMoZmRhdGFfZGZbZmRhdGFfZGYkTmVnYXRpdmU9PVRSVUUsXSkNCnRlbXBfZXhwPC1hc3NheURhdGFFbGVtZW50KERhdGEsZWx0PSdleHBycycpDQpuZWdwcm9iZV9leHByX2ZkPC10ZW1wX2V4cFtyb3duYW1lcyh0ZW1wX2V4cCkgJWluJSBuZWdwcm9iZXNuYW1lcyxdDQp0b3RfbmVnX2N0cmxfcmVhZHM8LWNvbFN1bXMobmVncHJvYmVfZXhwcl9mZCkNCnRvdF9kZWR1cF9yZWFkczwtcERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSREZWR1cGxpY2F0ZWRSZWFkcw0KDQpkZjwtZGF0YS5mcmFtZSgnYW9pJz0gbmFtZXModG90X25lZ19jdHJsX3JlYWRzKSwndG90X2RlZHVwX3JlYWRzJyA9IGFzLm51bWVyaWModG90X2RlZHVwX3JlYWRzKSwndG90X25lZ19jdHJsX3JlYWRzJz1hcy5udW1lcmljKHRvdF9uZWdfY3RybF9yZWFkcykpDQpkZjwtbWVsdChkZixpZD0iYW9pIikNCmdncGxvdChkZixhZXMoZmlsbD12YXJpYWJsZSx5PXZhbHVlLHg9YW9pKSkgKyANCiAgZ2VvbV9iYXIocG9zaXRpb249ImlkZW50aXR5IixzdGF0PSJpZGVudGl0eSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gbG9nMl90cmFucygpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIixheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSxheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSAgICAgICAgICAgICAgICAgICAgIA0KIA0KYGBgDQoNCiMjIER1cGxpY2F0ZWQgcmVhZHMgdnMgQmFja2dyb3VuZA0KDQpDb21wYXJlIGR1cGxpY2F0ZWQgcmVhZHMgYW5kIGJhY2tncm91bmQgbm9pc2UgdG8gc2hvdyB0aGF0IHRoZSByZWFkcyBzaWduaWZpY2FudGx5IGR1cGxpY2F0ZWQgYW5kIHRodXMgY2hlY2tlZCB0byBhdm9pZCB0aGUgcXVlc3Rpb25pbmcgYWJvdXQgdGhlIHZhbGlkaXR5IG9mIHRoZSByZWFkcy4NCg0KYGBge3J9DQojIGdldCBkY2MgcGVyIHBsYXRlLiBzdW0gbmVncHJvYmUgY291bnRzL2RjYy9wbGF0ZQ0KZ2dwbG90KHBEYXRhKHByb3RvY29sRGF0YShEYXRhKSksDQogICAgICAgYWVzKHggPSBQbGF0ZV9JRCwgZmlsbD1QbGF0ZV9JRCwNCiAgICAgICAgICB5ID0gRGVkdXBsaWNhdGVkUmVhZHMpKSArDQogIGdlb21fdmlvbGluKCkgKw0KICBnZW9tX2ppdHRlcih3aWR0aCA9IC4yKSArDQogIGxhYnMoeSA9ICJEZWR1cGxpY2F0ZWQgLyBSYXcgcmVhZHMiKSArDQogIHNjYWxlX3lfbG9nMTAoKSsNCiAgZ2VvbV9obGluZShkYXRhID1wRGF0YShwcm90b2NvbERhdGEoRGF0YSkpICwgDQogICAgICAgICAgIGFlcyh5aW50ZXJjZXB0ID0gTlRDLCBjb2xvdXI9UGxhdGVfSUQpKSArDQogIHRoZW1lX2J3KCkNCg0KYGBgDQoNCiMjIER1cGxpY2F0ZWQgcmVhZHMgdnMgUk9JYXJlYQ0KDQpTZWUgaWYgYWxsIFJPSXMgY29udGFpbiBkdXBsaWNhdGVkIHJlYWRzIGZvciB2YWxpZGl0eS4NCg0KYGBge3IsIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTV9DQp0ZW1wX2RmPC1jYmluZChwRGF0YShEYXRhKSxwRGF0YShwcm90b2NvbERhdGEoRGF0YSkpLGRjYz1yb3duYW1lcyhwRGF0YShEYXRhKSkpDQoNCmdncGxvdCh0ZW1wX2RmLA0KICAgICAgIGFlcyh4ID0gZGNjLCBjb2xvdXI9c2xpZGVfbmFtZSwNCiAgICAgICAgICB5ID0gKERlZHVwbGljYXRlZFJlYWRzL2FyZWEpICkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgeWxpbSgwLDIwKSArIA0KICBsYWJzKHkgPSAiRGVkdXBsaWNhdGVkIHJlYWRzIC8gUk9JIGFyZWEiKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPTYsIGFuZ2xlPTkwLCBoanVzdD0xKSApDQoNCmBgYA0KDQojIyBEdXBsaWNhdGVkIHJlYWRzIHZzIG51Y2xlaQ0KDQpTaG93IHRoZSBhbW91bnQgb2YgZGVkdXBsaWNhdGVkIHJlYWRzIHBlciBudWNsZWkuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD01fQ0KdGVtcF9kZjwtY2JpbmQocERhdGEoRGF0YSkscERhdGEocHJvdG9jb2xEYXRhKERhdGEpKSxkY2M9cm93bmFtZXMocERhdGEoRGF0YSkpKQ0KDQpnZ3Bsb3QodGVtcF9kZiwNCiAgICAgICBhZXMoeCA9IGRjYywgY29sb3VyPXNsaWRlX25hbWUsDQogICAgICAgICAgeSA9IChEZWR1cGxpY2F0ZWRSZWFkcy9udWNsZWkpICkpICsNCiAgZ2VvbV9wb2ludCgpICsNCiAgeWxpbSgwLDUwKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBZGp1c3QgcGVyIHByb2plY3QNCiAgbGFicyh5ID0gIkRlZHVwbGljYXRlZCByZWFkcyAvIG51Y2xlaSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9NiwgYW5nbGU9OTAsIGhqdXN0PTEpICkNCg0KYGBgDQoNCiMgNC4zIFByb2Nlc3MgTmVnYXRpdmUgR2VvTWVhbnMNCg0KTmVnYXRpdmUgQ291bnQ6IHRoaXMgaXMgdGhlIGdlb21ldHJpYyBtZWFuIG9mIHRoZSBzZXZlcmFsIHVuaXF1ZSBuZWdhdGl2ZSBwcm9iZXMgaW4gdGhlIEdlb014IHBhbmVsIHRoYXQgZG8gbm90IHRhcmdldCBtUk5BIGFuZCBlc3RhYmxpc2ggdGhlIGJhY2tncm91bmQgY291bnQgbGV2ZWwgcGVyIHNlZ21lbnQ7IHNlZ21lbnRzIHdpdGggbG93IG5lZ2F0aXZlIGNvdW50cyAoMS0xMCkgYXJlIG5vdCBuZWNlc3NhcmlseSByZW1vdmVkIGJ1dCBtYXkgYmUgc3R1ZGllZCBjbG9zZXIgZm9yIGxvdyBlbmRvZ2Vub3VzIGdlbmUgc2lnbmFsIGFuZC9vciBpbnN1ZmZpY2llbnQgdGlzc3VlIHNhbXBsaW5nLg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBuZWdhdGl2ZSBnZW9tZXRyaWMgbWVhbnMgZm9yIGVhY2ggbW9kdWxlDQojIEl0IHdpbGwgc2hvdyBvbmx5IHRoZSBuZWdhdGl2ZSBwcm9iZXMgZ2VvbWVhbiwgc28gZXhwZWN0IGxlc3Mgc2VnbWVudHMuDQpuZWdhdGl2ZUdlb01lYW5zIDwtIA0KICBlc0J5KG5lZ2F0aXZlQ29udHJvbFN1YnNldChEYXRhKSwgDQogICAgICAgR1JPVVAgPSAiTW9kdWxlIiwgDQogICAgICAgRlVOID0gZnVuY3Rpb24oeCkgeyANCiAgICAgICAgIGFzc2F5RGF0YUFwcGx5KHgsIE1BUkdJTiA9IDIsIEZVTiA9IG5nZW9NZWFuLCBlbHQgPSAiZXhwcnMiKSANCiAgICAgICB9KSANCnByb3RvY29sRGF0YShEYXRhKVtbIk5lZ0dlb01lYW4iXV0gPC0gbmVnYXRpdmVHZW9NZWFucw0KDQpuZWdDb2xzIDwtIHBhc3RlMCgiTmVnR2VvTWVhbl8iLCBtb2R1bGVzKQ0KcERhdGEoRGF0YSlbLCBuZWdDb2xzXSA8LSBzRGF0YShEYXRhKVtbIk5lZ0dlb01lYW4iXV0NCmZvcihhbm4gaW4gbmVnQ29scykgew0KICBwbHQgPC0gUUNfaGlzdG9ncmFtKHBEYXRhKERhdGEpLCBhbm4sIGNvbF9ieSwgMiwgc2NhbGVfdHJhbnMgPSAibG9nMTAiKQ0KICBwcmludChwbHQpDQp9DQoNCg0KIyBEZXRhdGNoIG5lZ19nZW9tZWFuIGNvbHVtbnMgYWhlYWQgb2YgYWdncmVnYXRlQ291bnRzIGNhbGwNCg0KcERhdGEoRGF0YSkgPC0gcERhdGEoRGF0YSlbLCAhY29sbmFtZXMocERhdGEoRGF0YSkpICVpbiUgbmVnQ29sc10NCg0KYGBgDQoNClNob3cgYWxsIE5UQyB2YWx1ZXMsIEZyZXEgPSBcIyBvZiBTZWdtZW50cyB3aXRoIGEgZ2l2ZW4gTlRDIGNvdW50Og0KDQpgYGB7ciBRQ190YWJsZXN9DQpRQzwtc0RhdGEoRGF0YSkNCg0KbnRjX2RmIDwtUUNbLGMoInNsaWRlX25hbWUiLCJQbGF0ZV9JRCIsIk5UQ19JRCIsIk5UQyIpXQ0KdGVtcHRhYmxlPC1udGNfZGYgJT4lIGRwbHlyOjpjb3VudChudGNfZGYkc2xpZGVfbmFtZSxudGNfZGYkTlRDX0lELG50Y19kZiRQbGF0ZV9JRCxudGNfZGYkTlRDKQ0KY29sbmFtZXModGVtcHRhYmxlKSA8LSBjKCJTbGlkZV9uYW1lIiwiTlRDX0lEIiwiUGxhdGVfSUQiLCJOVENfY291bnQiLCJOdW1iZXJfb2Zfc2FtcGxlcyIpDQpkYXRhdGFibGUodGVtcHRhYmxlLCByb3duYW1lcyA9IEZBTFNFKQ0KDQoNCmthYmxlKHRhYmxlKE5UQ19Db3VudCA9IHNEYXRhKERhdGEpJE5UQyksIGNvbC5uYW1lcyA9IGMoIk5UQyBDb3VudCIsICIjIG9mIFNlZ21lbnRzIikpDQoNCmthYmxlKFFDX1N1bW1hcnksIGNhcHRpb24gPSAiUUMgU3VtbWFyeSBUYWJsZSBmb3IgZWFjaCBTZWdtZW50IikNCg0KZGF0YXRhYmxlKFFDX1N1bW1hcnksDQogICAgICAgICAgY2FwdGlvbiA9ICJBT0kgUUMgU3VtbWFyeSIsDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICApDQopDQpgYGANCg0KU2hvdyBBT0lzIHdoaWNoIGZhaWwgY3JpdGljYWwgUUNzLg0KDQpgYGB7ciBsaXN0X2ZhaWx1cmVzfQ0KUUM8LXNEYXRhKERhdGEpDQp1bmRlcnNhdDwtc3Vic2V0KFFDLCBgU2F0dXJhdGVkICglKWA8PSBRQ19wYXJhbXMkcGVyY2VudFNhdHVyYXRpb24pDQoNCmlmKG5yb3codW5kZXJzYXQpPiAwKSB7DQoNCmRhdGF0YWJsZShhZ2dyZWdhdGUodW5kZXJzYXQsIGJ5PWxpc3QodW5kZXJzYXQkU2FtcGxlSUQpLHBhc3RlLGNvbGxhcHNlPSI7IiksDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICApDQopfQ0KYGBgDQpTaG93IHRoZSByZWFzb25zIHdoeSB0aGUgQU9JcyBmYWlsIGNyaXRpY2FsIFFDcy4NCg0KYGBge3IgaW5mb19saXN0X2ZhaWx1cmVzfQ0KaWYobnJvdyh1bmRlcnNhdCk+IDApIHsNCg0KZGF0YXRhYmxlKGFnZ3JlZ2F0ZSh1bmRlcnNhdCRRQ0ZsYWdzLCBieT1saXN0KHVuZGVyc2F0JFNhbXBsZUlEKSxwYXN0ZSxjb2xsYXBzZT0iOyIpLA0KICAgICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgKSkgJT4lDQogICAgZm9ybWF0U3R5bGUoY29sdW1ucyA9IGNvbG5hbWVzKHVuZGVyc2F0JFFDRmxhZ3MpLCBiYWNrZ3JvdW5kQ29sb3IgPSBzdHlsZUVxdWFsKCJUUlVFIiwgIiM4NTAwMDAiKSwgY29sb3IgPSBzdHlsZUVxdWFsKCJUUlVFIiwgIndoaXRlIikpIA0KICB9DQpgYGANCg0KU3Vic2V0dGluZyBvdXIgZGF0YXNldCBoYXMgcmVtb3ZlZCBzYW1wbGVzIHdoaWNoIGRpZCBub3QgcGFzcyBRQw0KDQpgYGB7ciBzdWJzZXR0aW5nX1FDX2ZhaWxzfQ0KRGF0YSA8LSBEYXRhWywgUUNSZXN1bHRzJFFDU3RhdHVzID09ICJQQVNTIl0NCmBgYA0KDQpHZW5lcmFsbHkga2VlcCB0aGUgcWNDdXRvZmZzIHBhcmFtZXRlcnMgdW5jaGFuZ2VkLiBTZXQNCnJlbW92ZUxvY2FsT3V0bGllcnMgdG8gRkFMU0UgaWYgeW91IGRvIG5vdCB3YW50IHRvIHJlbW92ZSBsb2NhbCBvdXRsaWVycw0KDQpgYGB7ciBwcm9jZXNzX1FDfQ0KRGF0YSA8LSBzZXRCaW9Qcm9iZVFDRmxhZ3MoRGF0YSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcWNDdXRvZmZzID0gbGlzdChtaW5Qcm9iZVJhdGlvID0gMC4xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVyY2VudEZhaWxHcnViYnMgPSAyMCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZUxvY2FsT3V0bGllcnMgPSBGQUxTRSkNCg0KUHJvYmVRQ1Jlc3VsdHMgPC0gZkRhdGEoRGF0YSlbWyJRQ0ZsYWdzIl1dDQpgYGANCg0KRGVmaW5lIFFDIHRhYmxlIGZvciBQcm9iZSBRQw0KDQpgYGB7ciBkZWZpbmVfcWNfdGFibGV9DQpxY19kZiA8LSBkYXRhLmZyYW1lKFBhc3NlZCA9IHN1bShyb3dTdW1zKFByb2JlUUNSZXN1bHRzWywgLTFdKSA9PSAwKSwNCiAgICAgICAgICAgICAgICAgICAgR2xvYmFsID0gc3VtKFByb2JlUUNSZXN1bHRzJEdsb2JhbEdydWJic091dGxpZXIpLA0KICAgICAgICAgICAgICAgICAgICBMb2NhbCA9IHN1bShyb3dTdW1zKFByb2JlUUNSZXN1bHRzWywgLTI6LTFdKSA+IDANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJiAhUHJvYmVRQ1Jlc3VsdHMkR2xvYmFsR3J1YmJzT3V0bGllcikpDQpgYGANCg0KU3Vic2V0IG9iamVjdCB0byBleGNsdWRlIGFsbCB0aGF0IGRpZCBub3QgcGFzcyBSYXRpbyAmIEdsb2JhbCB0ZXN0aW5nDQoNCmBgYHtyIHN1YnNldH0NClByb2JlUUNQYXNzZWQgPC0gDQogIHN1YnNldChEYXRhLCANCiAgICAgICAgIGZEYXRhKERhdGEpW1siUUNGbGFncyJdXVssYygiTG93UHJvYmVSYXRpbyIpXSA9PSBGQUxTRSAmDQogICAgICAgICAgIGZEYXRhKERhdGEpW1siUUNGbGFncyJdXVssYygiR2xvYmFsR3J1YmJzT3V0bGllciIpXSA9PSBGQUxTRSkNCg0KRGF0YSA8LSBQcm9iZVFDUGFzc2VkIA0KY2F0KCJBZnRlciBRQyBmZWF0dXJlczoiLCBkaW0oRGF0YSlbMV0sICJcbkFmdGVyIFFDIHNhbXBsZXM6IiwgZGltKERhdGEpWzJdKQ0KYGBgDQoNCkNoZWNrIGhvdyBtYW55IHVuaXF1ZSB0YXJnZXRzIHRoZSBvYmplY3QgaGFzDQoNCmBgYHtyIHVuaXF1ZV9jaGVja30NCmxlbmd0aCh1bmlxdWUoZmVhdHVyZURhdGEoRGF0YSlbWyJUYXJnZXROYW1lIl1dKSkNCmBgYA0KDQpDb2xsYXBzZSB0byB0YXJnZXRzDQoNCmBgYHtyIGNvbGxhcHNfdGFyZ2V0c30NCnRhcmdldF9EYXRhIDwtIGFnZ3JlZ2F0ZUNvdW50cyhEYXRhKQ0KDQpleHBycyh0YXJnZXRfRGF0YSlbMTo1LCAxOjJdDQpgYGANCg0KRGVmaW5lIExPUSBTRCB0aHJlc2hvbGQgYW5kIG1pbmltdW0gdmFsdWUNCg0KYGBge3Igc2V0X0xTUX0NCmN1dG9mZiA8LSAyDQptaW5MT1EgPC0gMg0KYGBgDQoNCiMgNC40IExpbWl0IG9mIFF1YW50aWZpY2F0aW9uDQoNCldlIGRlZmluZSBhIGxpbWl0IG9mIHF1YW50aWZpY2F0aW9uIChMT1EpIHBlciBST0kvQU9JIHNlZ21lbnQgYmFzZWQgb24NCnRoZSBuZWdhdGl2ZSBjb250cm9sIHByb2JlcyB0byBndWlkZSB0aGUgZmlsdGVyaW5nIG9mIHNlZ21lbnRzIGFuZCBnZW5lcw0Kd2l0aCBsb3cgc2lnbmFsIHJlbGF0aXZlIHRvIGJhY2tncm91bmQuIFRoZSBmb3JtdWxhIGZvciBjYWxjdWxhdGluZyB0aGUNCkxPUSBpbiB0aGUgJGlee3RofSQgc2VnbWVudCBhdCAkbiQgc3RhbmRhcmQgZGV2aWF0aW9ucyAoJG4gPSAyJCBmb3IgdGhpcw0Kc3R1ZHkpIGlzOiAkTE9RX2k9Z2VvbWVhbihOZWdQcm9iZV9pKSpnZW9TRChOZWdQcm9iZV9pKV5uJA0KDQpDYWxjdWxhdGUgTE9RIHBlciBtb2R1bGUgdGVzdGVkDQoNCmBgYHtyIGNhbGN1bGF0ZV9MT1F9DQpMT1EgPC0gZGF0YS5mcmFtZShyb3cubmFtZXMgPSBjb2xuYW1lcyh0YXJnZXRfRGF0YSkpDQpmb3IobW9kdWxlIGluIG1vZHVsZXMpIHsNCiAgdmFycyA8LSBwYXN0ZTAoYygiTmVnR2VvTWVhbl8iLCAiTmVnR2VvU0RfIiksDQogICAgICAgICAgICAgICAgIG1vZHVsZSkNCiAgaWYoYWxsKHZhcnNbMToyXSAlaW4lIGNvbG5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpKSB7DQogICAgTE9RWywgbW9kdWxlXSA8LQ0KICAgICAgcG1heChtaW5MT1EsDQogICAgICAgICAgIHBEYXRhKHRhcmdldF9EYXRhKVssIHZhcnNbMV1dICogDQogICAgICAgICAgICAgcERhdGEodGFyZ2V0X0RhdGEpWywgdmFyc1syXV0gXiBjdXRvZmYpDQogIH0NCn0NCnBEYXRhKHRhcmdldF9EYXRhKSRMT1EgPC0gTE9RDQpgYGANCg0KIyA0LjUgRmlsdGVyaW5nDQoNCkFmdGVyIGRldGVybWluaW5nIHRoZSBsaW1pdCBvZiBxdWFudGlmaWNhdGlvbiAoTE9RKSBwZXIgc2VnbWVudCwgd2UNCnJlY29tbWVuZCBmaWx0ZXJpbmcgb3V0IGVpdGhlciBzZWdtZW50cyBhbmQvb3IgZ2VuZXMgd2l0aCBhYm5vcm1hbGx5IGxvdw0Kc2lnbmFsLiBGaWx0ZXJpbmcgaXMgYW4gaW1wb3J0YW50IHN0ZXAgdG8gZm9jdXMgb24gdGhlIHRydWUgYmlvbG9naWNhbA0KZGF0YSBvZiBpbnRlcmVzdC4NCg0KV2UgZGV0ZXJtaW5lIHRoZSBudW1iZXIgb2YgZ2VuZXMgZGV0ZWN0ZWQgaW4gZWFjaCBzZWdtZW50IGFjcm9zcyB0aGUNCmRhdGFzZXQuDQoNCmBgYHtyIGZpbHRlcmluZ30NCkxPUV9NYXQgPC0gYygpDQpmb3IobW9kdWxlIGluIG1vZHVsZXMpIHsNCiAgaW5kIDwtIGZEYXRhKHRhcmdldF9EYXRhKSRNb2R1bGUgPT0gbW9kdWxlDQogIE1hdF9pIDwtIHQoZXNBcHBseSh0YXJnZXRfRGF0YVtpbmQsIF0sIE1BUkdJTiA9IDEsDQogICAgICAgICAgICAgICAgICAgICBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgICAgICAgICAgICAgICAgICAgIHggPiBMT1FbLCBtb2R1bGVdDQogICAgICAgICAgICAgICAgICAgICB9KSkNCiAgTE9RX01hdCA8LSByYmluZChMT1FfTWF0LCBNYXRfaSkNCn0NCiMgZW5zdXJlIG9yZGVyaW5nIHNpbmNlIHRoaXMgaXMgc3RvcmVkIG91dHNpZGUgb2YgdGhlIGdlb214U2V0DQpMT1FfTWF0IDwtIExPUV9NYXRbZkRhdGEodGFyZ2V0X0RhdGEpJFRhcmdldE5hbWUsIF0NCmBgYA0KDQojIDQuNS4xIFNlZ21lbnQgR2VuZSBEZXRlY3Rpb24NCg0KV2UgZmlyc3QgZmlsdGVyIG91dCBzZWdtZW50cyB3aXRoIGV4Y2VwdGlvbmFsbHkgbG93IHNpZ25hbC4gVGhlc2UNCnNlZ21lbnRzIHdpbGwgaGF2ZSBhIHNtYWxsIGZyYWN0aW9uIG9mIHBhbmVsIGdlbmVzIGRldGVjdGVkIGFib3ZlIHRoZQ0KTE9RIHJlbGF0aXZlIHRvIHRoZSBvdGhlciBzZWdtZW50cyBpbiB0aGUgc3R1ZHkuIExldCdzIHZpc3VhbGl6ZSB0aGUNCmRpc3RyaWJ1dGlvbiBvZiBzZWdtZW50cyB3aXRoIHJlc3BlY3QgdG8gdGhlaXIgJSBnZW5lcyBkZXRlY3RlZDoNCg0KU2F2ZSBkZXRlY3Rpb24gcmF0ZSBpbmZvcm1hdGlvbiB0byBwaGVubyBkYXRhDQoNCmBgYHtyIHNhdmVfZGV0ZWN0aW5vX3JhdGV9DQpwRGF0YSh0YXJnZXRfRGF0YSkkR2VuZXNEZXRlY3RlZCA8LSANCiAgY29sU3VtcyhMT1FfTWF0LCBuYS5ybSA9IFRSVUUpDQpwRGF0YSh0YXJnZXRfRGF0YSkkR2VuZURldGVjdGlvblJhdGUgPC0NCiAgcERhdGEodGFyZ2V0X0RhdGEpJEdlbmVzRGV0ZWN0ZWQgLyBucm93KHRhcmdldF9EYXRhKQ0KDQpgYGANCg0KRGV0ZXJtaW5lIGRldGVjdGlvbiB0aHJlc2hvbGRzOiAxJSwgNSUsIDEwJSwgMTUlLCBcPjE1JQ0KDQpgYGB7ciBkZXRlcm1pbmUrdGhyZXNob2xkc30NCnBEYXRhKHRhcmdldF9EYXRhKSREZXRlY3Rpb25UaHJlc2hvbGQgPC0gDQogIGN1dChwRGF0YSh0YXJnZXRfRGF0YSkkR2VuZURldGVjdGlvblJhdGUsDQogICAgICBicmVha3MgPSBjKDAsIDAuMDEsIDAuMDUsIDAuMSwgMC4xNSwgMC4yLDEpLA0KICAgICAgbGFiZWxzID0gYygiPDElIiwgIjEtNSUiLCAiNS0xMCUiLCAiMTAtMTUlIiwgIjE1LTIwJSIsICI+MjAlIikpDQoNCiMgc3RhY2tlZCBiYXIgcGxvdCBvZiBkaWZmZXJlbnQgY3V0IHBvaW50cyAoMSUsIDUlLCAxMCUsIDE1JSkNCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSBEZXRlY3Rpb25UaHJlc2hvbGQpKSArDQogIGdlb21fYmFyKGFlcyhmaWxsID0gQU5OMikpICsNCiAgZ2VvbV90ZXh0KHN0YXQgPSAiY291bnQiLCBhZXMobGFiZWwgPSAuLmNvdW50Li4pLCB2anVzdCA9IC0wLjUpICsNCiAgdGhlbWVfYncoKSArDQogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4xKSkpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbG9yX2xpc3QkQU5OMikgKw0KICBsYWJzKHggPSAiR2VuZSBEZXRlY3Rpb24gUmF0ZSIsDQogICAgICAgeSA9ICJTZWdtZW50cywgIyIsDQogICAgICAgZmlsbCA9ICJTZWdtZW50IFR5cGUiKQ0KYGBgDQoNCmN1dCBwZXJjZW50IGdlbmVzIGRldGVjdGVkIGF0IDEsIDUsIDEwLCAxNQ0KDQpgYGB7ciBjdXRfdG9fcGVyY2VudH0NCmthYmxlKHRhYmxlKHBEYXRhKHRhcmdldF9EYXRhKSREZXRlY3Rpb25UaHJlc2hvbGQsDQogICAgICAgICAgICBwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMikpDQoNCiMgc2V0IHRocmVzaG9sZCBmb3IgZGV0ZWN0aW9ubGV2ZWwNCiMgZGVmYXVsdCAwLjENCmdlbmVfZGV0X3RocmVzaG9sZCA8LSAwLjA1DQoNCnRhcmdldF9EYXRhIDwtDQogIHRhcmdldF9EYXRhWywgcERhdGEodGFyZ2V0X0RhdGEpJEdlbmVEZXRlY3Rpb25SYXRlID49IGdlbmVfZGV0X3RocmVzaG9sZF0NCg0KZGltKHRhcmdldF9EYXRhKQ0KYGBgDQoNCiMgNC42IE1hbnVhbCByZW1vdmFsIG9mIHNhbXBsZXMvY2xhc3Nlcw0KDQpgYGB7ciByZW1vdmVfc2FtcGxlc30NCmFjdGl2ZV9hb2lzPC1uYW1lcyhhcy5kYXRhLmZyYW1lKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEsIGVsdD0gImV4cHJzIikpKQ0KYGBgDQoNCnJlLUNvbGxlY3QgYW5ub3RhdGlvbnMNCg0KYGBge3IgY29sbGVjdF9hbm5vdGF0aW9uc30NCiMgZ2F0aGVyIHRoZSBkYXRhIGFuZCBwbG90IGluIG9yZGVyOiBjbGFzcywgc2xpZGUgbmFtZSwgcmVnaW9uLCBzZWdtZW50DQojY291bnRfbWF0IDwtIGRwbHlyOjpjb3VudChwRGF0YShEYXRhKSwgQU5OMSxBTk4yLEFOTjMsQU5ONCxzbGlkZV9uYW1lKQ0KDQp0ZW1wX3FjIDwtIHRlbXBfZGYNCnRlbXBfcWMkUUNSZXN1bHQgPC0gUUNSZXN1bHRzJFFDU3RhdHVzDQp0ZW1wX3FjJFFDUmVzdWx0W3RlbXBfcWMkUUNSZXN1bHQgPT0gIldBUk5JTkciXSA8LSAiWCINCg0KI2NvdW50X21hdCA8LSBkcGx5cjo6Y291bnQoYSwgQU5OMSwgQU5OMiwgc2xpZGVfbmFtZSwgUUNSZXN1bHQpDQpjb3VudF9tYXQgPC0gdGVtcF9xYyAlPiUgZHBseXI6OmNvdW50KHRlbXBfcWNbYyhhbm5fbmFtZXMsICJRQ1Jlc3VsdCIpXSkNCg0KdGVzdF9nciA8LSBnYXRoZXJfc2V0X2RhdGEoY291bnRfbWF0LCAxOihhbm5fc2l6ZSsxKSkNCnRlc3RfZ3IkeCA8LSBmYWN0b3IodGVzdF9nciR4LCBsYWJlbHMgPSBjKGFubl9uYW1lcywgIlFDUmVzdWx0IikpDQpgYGANCg0KcmUtUGxvdCBTYW5rZXkNCg0KYGBge3IgcGxvdF9zYW5rZXksIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTExfQ0KZ2dwbG90KHRlc3RfZ3IsIGFlcyh4LCBpZCA9IGlkLCBzcGxpdCA9IHksIHZhbHVlID0gbikpICsNCiAgZ2VvbV9wYXJhbGxlbF9zZXRzKGFlcyhmaWxsID0gQU5OMiksIGFscGhhID0gMC41LCBheGlzLndpZHRoID0gMC4xKSArDQogIGdlb21fcGFyYWxsZWxfc2V0c19heGVzKGF4aXMud2lkdGggPSAwLjIpICsNCiAgZ2VvbV9wYXJhbGxlbF9zZXRzX2xhYmVscyhjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSA1KSArDQogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTcpICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbigwKSkgKyANCiAgc2NhbGVfeF9kaXNjcmV0ZShleHBhbmQgPSBleHBhbnNpb24oMCkpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNvbG9yX2xpc3QkQU5OMikgKw0KICBsYWJzKHggPSAiIiwgeSA9ICIiKSArDQogIGFubm90YXRlKGdlb20gPSAic2VnbWVudCIsIHggPSAzLjI1LCB4ZW5kID0gMy4yNSwgeSA9IDEwLCANCiAgICAgICAgICAgeWVuZCA9IDYwLCBsd2QgPSAyKSArDQogIGFubm90YXRlKGdlb20gPSAidGV4dCIsIHggPSAzLjE5LCB5ID0gMjUsIGFuZ2xlID0gOTAsIHNpemUgPSA1LA0KICAgICAgICAgICBoanVzdCA9IDAuNSwgbGFiZWwgPSAiNTAgc2VnbWVudHMiKQ0KYGBgDQoNCiMgNC43IEdlbmUgRGV0ZWN0aW9uIFJhdGUNCg0KQ2FsY3VsYXRlIGRldGVjdGlvbiByYXRlDQoNCmBgYHtyIGNsY19kZXRlY3Rpb25fcmF0ZX0NCkxPUV9NYXQgPC0gTE9RX01hdFssIGNvbG5hbWVzKHRhcmdldF9EYXRhKV0NCmZEYXRhKHRhcmdldF9EYXRhKSREZXRlY3RlZFNlZ21lbnRzIDwtIHJvd1N1bXMoTE9RX01hdCwgbmEucm0gPSBUUlVFKQ0KZkRhdGEodGFyZ2V0X0RhdGEpJERldGVjdGlvblJhdGUgPC0NCiAgZkRhdGEodGFyZ2V0X0RhdGEpJERldGVjdGVkU2VnbWVudHMgLyBucm93KHBEYXRhKHRhcmdldF9EYXRhKSkNCmBgYA0KDQpHZW5lIG9mIGludGVyZXN0IGRldGVjdGlvbiB0YWJsZQ0KDQpgYGB7ciBnZW5lX29mX2ludGVyZXN0X3RhYmxlfQ0KZ29pIDwtIGMoIlBEQ0QxIiwgIkNEMjc0IiwgIklGTkciLCAiQ0Q4QSIsICJDRDY4IiwgIkVQQ0FNIiwNCiAgICAgICAgICJLUlQxOCIsICJOUEhTMSIsICJOUEhTMiIsICJDQUxCMSIsICJDTEROOCIpDQpnb2lfZGYgPC0gZGF0YS5mcmFtZSgNCiAgR2VuZSA9IGdvaSwNCiAgTnVtYmVyID0gZkRhdGEodGFyZ2V0X0RhdGEpW2dvaSwgIkRldGVjdGVkU2VnbWVudHMiXSwNCiAgRGV0ZWN0aW9uUmF0ZSA9IHBlcmNlbnQoZkRhdGEodGFyZ2V0X0RhdGEpW2dvaSwgIkRldGVjdGlvblJhdGUiXSkpDQpgYGANCg0KIyA0LjggR2VuZSBGaWx0ZXJpbmcNCg0KV2Ugd2lsbCBncmFwaCB0aGUgdG90YWwgbnVtYmVyIG9mIGdlbmVzIGRldGVjdGVkIGluIGRpZmZlcmVudA0KcGVyY2VudGFnZXMgb2Ygc2VnbWVudHMuIEJhc2VkIG9uIHRoZSB2aXN1YWxpemF0aW9uIGJlbG93LCB3ZSBjYW4gYmV0dGVyDQp1bmRlcnN0YW5kIGdsb2JhbCBnZW5lIGRldGVjdGlvbiBpbiBvdXIgc3R1ZHkgYW5kIHNlbGVjdCBob3cgbWFueSBsb3cNCmRldGVjdGVkIGdlbmVzIHRvIGZpbHRlciBvdXQgb2YgdGhlIGRhdGFzZXQuIEdlbmUgZmlsdGVyaW5nIGluY3JlYXNlcw0KcGVyZm9ybWFuY2Ugb2YgZG93bnN0cmVhbSBzdGF0aXN0aWNhbCB0ZXN0cyBhbmQgaW1wcm92ZXMgaW50ZXJwcmV0YXRpb24NCm9mIHRydWUgYmlvbG9naWNhbCBzaWduYWwuDQoNClBsb3QgZGV0ZWN0aW9uIHJhdGUNCg0KYGBge3IgcGxvdF9kZXRfcmF0ZX0NCnBsb3RfZGV0ZWN0IDwtIGRhdGEuZnJhbWUoRnJlcSA9IGMoMSwgNSwgMTAsIDIwLCAzMCwgNTApKQ0KDQojIE1hbnVhbCBnZW5lIGN1dC1vZmYNCmN1dG9mZiA8LSA1DQpjdXRvZmYgPC0gd2hpY2goc2FwcGx5KHBsb3RfZGV0ZWN0JEZyZXEsIEZVTj1mdW5jdGlvbihYKSBjdXRvZmYgJWluJSBYKSkgKyAwLjUgIyBTZWxlY3Qgc3RlcCBmcm9tIHBsb3RfZGV0ZWN0LiAxID0gMSwgNSA9IDIsIDEwID0gMywgZXRjLiArMC41IHRvIGRvZGdlIGJpbi4NCg0KcGxvdF9kZXRlY3QkTnVtYmVyIDwtDQogIHVubGlzdChsYXBwbHkoYygwLjAxLCAwLjA1LCAwLjEsIDAuMiwgMC4zLCAwLjUpLA0KICAgICAgICAgICAgICAgIGZ1bmN0aW9uKHgpIHtzdW0oZkRhdGEodGFyZ2V0X0RhdGEpJERldGVjdGlvblJhdGUgPj0geCl9KSkNCnBsb3RfZGV0ZWN0JFJhdGUgPC0gcGxvdF9kZXRlY3QkTnVtYmVyIC8gbnJvdyhmRGF0YSh0YXJnZXRfRGF0YSkpDQpyb3duYW1lcyhwbG90X2RldGVjdCkgPC0gcGxvdF9kZXRlY3QkRnJlcQ0KDQpnZ3Bsb3QocGxvdF9kZXRlY3QsIGFlcyh4ID0gYXMuZmFjdG9yKEZyZXEpLCB5ID0gUmF0ZSwgZmlsbCA9IFJhdGUpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBmb3JtYXRDKE51bWJlciwgZm9ybWF0ID0gImQiLCBiaWcubWFyayA9ICIsIikpLA0KICAgICAgICAgICAgdmp1c3QgPSAxLjYsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDQpICsNCiAgDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDMuNSwgY29sb3IgPSAicmVkIikgKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjdXRvZmYsIGNvbG9yID0gInJlZCIpICsNCiAgYW5ub3RhdGUoeD0zLjUseT0wLjEsbGFiZWw9IkRlZmF1bHQgY3V0LW9mZiIsc2l6ZT0zLGdlb209ImxhYmVsIikgKw0KICBhbm5vdGF0ZSh4PWN1dG9mZix5PTAuMSxsYWJlbD0iTWFudWFsIGN1dC1vZmYiLHNpemU9MyxnZW9tPSJsYWJlbCIpICsNCiAgDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJvcmFuZ2UyIiwgbWlkID0gImxpZ2h0Ymx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAiZG9kZ2VyYmx1ZTMiLCBtaWRwb2ludCA9IDAuNjUsDQogICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50KSArDQogIHRoZW1lX2J3KCkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpwZXJjZW50LCBsaW1pdHMgPSBjKDAsMSksDQogICAgICAgICAgICAgICAgICAgICBleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMCkpKSArDQogIGxhYnMoeCA9ICIlIG9mIFNlZ21lbnRzIiwNCiAgICAgICB5ID0gIkdlbmVzIERldGVjdGVkLCAlIG9mIFBhbmVsID4gTE9RIikNCmBgYA0KDQpTdWJzZXQgdG8gdGFyZ2V0IGdlbmVzIGRldGVjdGVkIGluIGF0IGxlYXN0IGEgbWFudWFsIHBlcmNlbnRhZ2Ugb2YgdGhlIHNhbXBsZXMgd2l0aCBhIGRlZmF1bHQgb2YgMTAlLiBBbHNvDQptYW51YWxseSBpbmNsdWRlIHRoZSBuZWdhdGl2ZSBjb250cm9sIHByb2JlLCBmb3IgZG93bnN0cmVhbSB1c2UuDQoNCmBgYHtyIHN1YnNldF90b18xMHBfZGV0ZWN0ZWRfZ2VuZXN9DQojIGRlZmF1bHQ9MC4xDQpuZWdhdGl2ZVByb2JlZkRhdGEgPC0gc3Vic2V0KGZEYXRhKHRhcmdldF9EYXRhKSwgQ29kZUNsYXNzID09ICJOZWdhdGl2ZSIpDQpuZWdfcHJvYmVzIDwtIHVuaXF1ZShuZWdhdGl2ZVByb2JlZkRhdGEkVGFyZ2V0TmFtZSkNCnRhcmdldF9EYXRhIDwtIA0KICB0YXJnZXRfRGF0YVtmRGF0YSh0YXJnZXRfRGF0YSkkRGV0ZWN0aW9uUmF0ZSA+PSAwLjA1IHwNCiAgICAgICAgICAgICAgICAgICAgZkRhdGEodGFyZ2V0X0RhdGEpJFRhcmdldE5hbWUgJWluJSBuZWdfcHJvYmVzLCBdDQoNCiMgcmV0YWluIG9ubHkgZGV0ZWN0ZWQgZ2VuZXMgb2YgaW50ZXJlc3QNCmdvaSA8LSBnb2lbZ29pICVpbiUgcm93bmFtZXModGFyZ2V0X0RhdGEpXQ0KDQpkaW0odGFyZ2V0X0RhdGEpDQpgYGANCg0KIyA1IE5vcm1hbGl6YXRpb24NCg0KV2Ugd2lsbCBub3cgbm9ybWFsaXplIHRoZSBHZW9NeCBkYXRhIGZvciBkb3duc3RyZWFtIHZpc3VhbGl6YXRpb25zIGFuZA0KZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uIFRoZSB0d28gY29tbW9uIG1ldGhvZHMgZm9yIG5vcm1hbGl6YXRpb24gb2YNCkRTUC1OR1MgUk5BIGRhdGEgYXJlIGkpIHF1YXJ0aWxlIDMgKFEzKSBvciBpaSkgYmFja2dyb3VuZCBub3JtYWxpemF0aW9uLg0KDQpCb3RoIG9mIHRoZXNlIG5vcm1hbGl6YXRpb24gbWV0aG9kcyBlc3RpbWF0ZSBhIG5vcm1hbGl6YXRpb24gZmFjdG9yIHBlcg0Kc2VnbWVudCB0byBicmluZyB0aGUgc2VnbWVudCBkYXRhIGRpc3RyaWJ1dGlvbnMgdG9nZXRoZXIuIE1vcmUgYWR2YW5jZWQNCm1ldGhvZHMgZm9yIG5vcm1hbGl6YXRpb24gYW5kIG1vZGVsaW5nIGFyZSB1bmRlciBhY3RpdmUgZGV2ZWxvcG1lbnQuDQpIb3dldmVyLCBmb3IgbW9zdCBzdHVkaWVzLCB0aGVzZSBtZXRob2RzIGFyZSBzdWZmaWNpZW50IGZvcg0KdW5kZXJzdGFuZGluZyBkaWZmZXJlbmNlcyBiZXR3ZWVuIGJpb2xvZ2ljYWwgY2xhc3NlcyBvZiBzZWdtZW50cyBhbmQNCnNhbXBsZXMuDQoNClEzIG5vcm1hbGl6YXRpb24gaXMgdHlwaWNhbGx5IHRoZSBwcmVmZXJyZWQgbm9ybWFsaXphdGlvbiBzdHJhdGVneSBmb3INCm1vc3QgRFNQLU5HUyBSTkEgc3R1ZGllcy4gR2l2ZW4gdGhlIGxvdyBuZWdhdGl2ZSBwcm9iZSBjb3VudHMgaW4gdGhpcw0KcGFydGljdWxhciBkYXRhc2V0IGFzIHNob3duIGR1cmluZyBTZWdtZW50IFFDLCB3ZSB3b3VsZCBmdXJ0aGVyIGF2b2lkDQpiYWNrZ3JvdW5kIG5vcm1hbGl6YXRpb24gYXMgaXQgbWF5IGJlIGxlc3Mgc3RhYmxlLg0KDQpCZWZvcmUgbm9ybWFsaXphdGlvbiwgd2Ugd2lsbCBleHBsb3JlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdXBwZXINCnF1YXJ0aWxlIChRMykgb2YgdGhlIGNvdW50cyBpbiBlYWNoIHNlZ21lbnQgd2l0aCB0aGUgZ2VvbWV0cmljIG1lYW4gb2YNCnRoZSBuZWdhdGl2ZSBjb250cm9sIHByb2JlcyBpbiB0aGUgZGF0YS4gSWRlYWxseSwgdGhlcmUgc2hvdWxkIGJlIGENCnNlcGFyYXRpb24gYmV0d2VlbiB0aGVzZSB0d28gdmFsdWVzIHRvIGVuc3VyZSB3ZSBoYXZlIHN0YWJsZSBtZWFzdXJlIG9mDQpRMyBzaWduYWwuIElmIHlvdSBkbyBub3Qgc2VlIHN1ZmZpY2llbnQgc2VwYXJhdGlvbiBiZXR3ZWVuIHRoZXNlIHZhbHVlcywNCnlvdSBtYXkgY29uc2lkZXIgbW9yZSBhZ2dyZXNzaXZlIGZpbHRlcmluZyBvZiBsb3cgc2lnbmFsIHNlZ21lbnRzL2dlbmVzLg0KDQpRMyBhbmQgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiBhcmUgcmFkaWNhbGx5IGRpZmZlcmVudCBpbiB0aGVpciBhcHByb2FjaC4gDQpRMyBub3JtYWxpemF0aW9uIHJlbGllcyBhIG5vcm1hbGl6YXRpb24gZmFjdG9yIHRoYXQgYWxpZ25zIHRoZSANCnRoaXJkIHF1YXJ0aWxlIGdlbmUgY291bnQgdmFsdWUgZm9yIGFsbCBzYW1wbGVzLiBUaGlzIG1ldGhvZCBkb2VzIG5vdCBhZGRyZXNzIA0KcG90ZW50aWFsIGdsb2JhbCB2YXJpYXRpb25zIGluIGRhdGEgZGlzdHJpYnV0aW9ucyBvciBleHByZXNzaW9uIHJhbmdlcy4gDQpJbiBjb250cmFzdCwgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiwgd2hpY2ggaXMgd2lkZWx5IHVzZWQgdG8gbm9ybWFsaXplIGdlbmUgDQpleHByZXNzaW9uIGRhdGEgZGVyaXZlZCBmcm9tIG1pY3JvYXJyYXlzLCBhbGlnbnMgY291bnQgdmFsdWVzIGFjY29yZGluZyB0byANCnRoZSByYW5rIG9mIHRoZSBnZW5lcyBhbmQgZm9yY2VzIHRoZSBkYXRhIG9mIGFsbCBzYW1wbGVzIGludG8gdGhlIHNhbWUgDQpkaXN0cmlidXRpb24uIEl0IGlzIHRoZXJlZm9yZSBpbmhlcmVudCB0byB0aGlzIG1ldGhvZCB0aGF0IHRoZSBkaWZmZXJlbmNlIA0KaW4gY291bnQgdmFsdWUgcmFuZ2Ugb3Igc2lnbmFsLXRvLW5vaXNlIHJhdGlvIGlzIG5vIGxvbmdlciBkZXBlbmRlbnQgb24gDQpyYXcgZGF0YSBkaXN0cmlidXRpb25zWzFdLg0KDQpub3RlIHRoYXQgcXVhbnRpbGUgbm9ybWFsaXphdGlvbiwgZHVlIHRvIGl0cyBjb3Vyc2UgbWV0aG9kIG9mIG5vcm1hbGl6YXRpb24sIA0KbWF5IGxpbWl0IHRoZSBkZXRlY3Rpb24gb2YgbW9yZSBzdWJ0bGUgZGlmZmVyZW5jZXMgaW4gZ2VuZSBleHByZXNzaW9uLg0KDQpHcmFwaCBRMyB2YWx1ZSB2cyBuZWdHZW9NZWFuIG9mIE5lZ2F0aXZlcw0KDQpgYGB7ciBscG90X3EzX25lZ0dlb01lYW59DQphbm5fb2ZfaW50ZXJlc3QgPC0gIkFOTjIiDQpTdGF0X2RhdGEgPC0gDQogIGRhdGEuZnJhbWUocm93Lm5hbWVzID0gY29sbmFtZXMoZXhwcnModGFyZ2V0X0RhdGEpKSwNCiAgICAgICAgICAgICBTZWdtZW50ID0gY29sbmFtZXMoZXhwcnModGFyZ2V0X0RhdGEpKSwNCiAgICAgICAgICAgICBBbm5vdGF0aW9uID0gcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX29mX2ludGVyZXN0XSwNCiAgICAgICAgICAgICBRMyA9IHVubGlzdChhcHBseShleHBycyh0YXJnZXRfRGF0YSksIDIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUsIDAuNzUsIG5hLnJtID0gVFJVRSkpLA0KICAgICAgICAgICAgIE5lZ1Byb2JlID0gZXhwcnModGFyZ2V0X0RhdGEpW25lZ19wcm9iZXMsIF0pDQpTdGF0X2RhdGFfbSA8LSBtZWx0KFN0YXRfZGF0YSwgbWVhc3VyZS52YXJzID0gYygiUTMiLCAiTmVnUHJvYmUiKSwNCiAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJTdGF0aXN0aWMiLCB2YWx1ZS5uYW1lID0gIlZhbHVlIikNCg0KcGx0MSA8LSBnZ3Bsb3QoU3RhdF9kYXRhX20sDQogICAgICAgICAgICAgICBhZXMoeCA9IFZhbHVlLCBmaWxsID0gU3RhdGlzdGljKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNDApICsgdGhlbWVfYncoKSArDQogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKw0KICBmYWNldF93cmFwKH5Bbm5vdGF0aW9uLCBucm93ID0gMSkgKyANCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9IDMsIHR5cGUgPSAicXVhbCIpICsNCiAgbGFicyh4ID0gIkNvdW50cyIsIHkgPSAiU2VnbWVudHMsICMiKQ0KDQpwbHQyIDwtIGdncGxvdChTdGF0X2RhdGEsDQogICAgICAgICAgICAgICBhZXMoeCA9IE5lZ1Byb2JlLCB5ID0gUTMsIGNvbG9yID0gQW5ub3RhdGlvbikpICsNCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBsdHkgPSAiZGFzaGVkIiwgY29sb3IgPSAiZGFya2dyYXkiKSArDQogIGdlb21fcG9pbnQoKSArIGd1aWRlcyhjb2xvciA9ICJub25lIikgKyB0aGVtZV9idygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpICsNCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKw0KICBsYWJzKHggPSAiTmVnYXRpdmUgUHJvYmUgR2VvTWVhbiwgQ291bnRzIiwgeSA9ICJRMyBWYWx1ZSwgQ291bnRzIikNCg0KcGx0MyA8LSBnZ3Bsb3QoU3RhdF9kYXRhLA0KICAgICAgICAgICAgICAgYWVzKHggPSBOZWdQcm9iZSwgeSA9IFEzIC8gTmVnUHJvYmUsIGNvbG9yID0gQW5ub3RhdGlvbikpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMSwgbHR5ID0gImRhc2hlZCIsIGNvbG9yID0gImRhcmtncmF5IikgKw0KICBnZW9tX3BvaW50KCkgKyB0aGVtZV9idygpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArIA0KICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpICsNCiAgdGhlbWUoYXNwZWN0LnJhdGlvID0gMSkgKw0KICBsYWJzKHggPSAiTmVnYXRpdmUgUHJvYmUgR2VvTWVhbiwgQ291bnRzIiwgeSA9ICJRMy9OZWdQcm9iZSBWYWx1ZSwgQ291bnRzIikNCg0KYnRtX3JvdyA8LSBwbG90X2dyaWQocGx0MiwgcGx0MywgbnJvdyA9IDEsIGxhYmVscyA9IGMoIkIiLCAiIiksDQogICAgICAgICAgICAgICAgICAgICByZWxfd2lkdGhzID0gYygwLjQzLDAuNTcpKQ0KcGxvdF9ncmlkKHBsdDEsIGJ0bV9yb3csIG5jb2wgPSAxLCBsYWJlbHMgPSBjKCJBIiwgIiIpKQ0KDQpgYGANCg0KUTMgbm9ybSAoNzV0aCBwZXJjZW50aWxlKSBmb3IgV1RBL0NUQSB3aXRoIG9yIHdpdGhvdXQgY3VzdG9tIHNwaWtlLWlucw0KDQpgYGB7ciBxM19ub3JtfQ0KdGFyZ2V0X0RhdGEgPC0gbm9ybWFsaXplKHRhcmdldF9EYXRhICwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9tZXRob2QgPSAicXVhbnQiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVzaXJlZFF1YW50aWxlID0gLjc1LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b0VsdCA9ICJxX25vcm0iKQ0KIywgZGF0YV90eXBlID0gIlJOQSIgZGVwcmljYXRlZCBhZnRlciA0LjENCg0KYGBgDQoNClF1YW50aWxlIE5vcm1hbGl6YXRpb24NCg0KYGBge3IgcXVhbnRpbGV9DQpxdWFudGlsZSA8LSBub3JtYWxpemUucXVhbnRpbGVzKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dKQ0Kcm93bmFtZXMocXVhbnRpbGUpIDwtIHJvd25hbWVzKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dKQ0KY29sbmFtZXMocXVhbnRpbGUpIDwtIGNvbG5hbWVzKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dKQ0KDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAicXVhbnRpbGVfbm9ybSIpIDwtICBxdWFudGlsZQ0KYGBgDQoNCkJhY2tncm91bmQgbm9ybWFsaXphdGlvbiBmb3IgV1RBL0NUQSB3aXRob3V0IGN1c3RvbSBzcGlrZS1pbg0KDQpgYGB7ciBiZ19ub3JtfQ0KdGFyZ2V0X0RhdGEgPC0gbm9ybWFsaXplKHRhcmdldF9EYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX21ldGhvZCA9ICJuZWciLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZnJvbUVsdCA9ICJleHBycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvRWx0ID0gIm5lZ19ub3JtIikNCg0KIyAsIGRhdGFfdHlwZSA9ICJSTkEiIGRlcHJpY2F0ZWQgYWZ0ZXIgNC4xDQpgYGANCg0KT3ZlcndyaXRlIHRoZSBkYWZhdWx0IHFfbm9ybSB3aXRoIGFub3RoZXIgbm9ybWFsaXphdGlvbiBtZXRob2QgaWYgbmVlZGVkDQoNCmBgYHtyIG92ZXJ3cml0ZSBxX25vcm19DQojIE5vcm1hbGl6YXRpb24gb3B0aW9ucyBhcmU6IGRlZmF1bHQgInFfbm9ybSIsICJuZWdfbm9ybSIgb3IgInF1YW50aWxlX25vcm0iLg0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gInFfbm9ybSIpIDwtIGFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJxX25vcm0iKSANCmBgYA0KDQojIDUuMSBWaXN1YWxpemUgdGhlIGZpcnN0IDEwIHNlZ21lbnRzIHdpdGggZWFjaCBub3JtYWxpemF0aW9uIG1ldGhvZCB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpVc2UgdGhlIHRhYi1tZW51IHRvIG5hdmlnYXRlIQ0KDQpgYGB7ciB2aXN1bGFpemVfbm9ybXN9DQoNCiNGaXggemVybyB2YWx1ZXMsIHdoaWNoIGdvIHRvIC1pbmYgaW4gbG9nIHRyYW5zZm9ybSBpbiBzdGFuZGFyZCBib3hwbG90DQojIHRlbXAgPC1hcy5tYXRyaXgoZXhwcnMoKHRhcmdldF9EYXRhKVssMToxMF0pKQ0KIyBsb25nIDwtIG1lbHQodGVtcCkNCiMgY29sbmFtZXMobG9uZykgPC0gYygiZ2VuZSIsInNlZ21lbnQiLCJjb3VudCIpDQojIGdncGxvdChsb25nLCBhZXMoeD1zZWdtZW50LHk9Y291bnQpKSArDQojICAgICBnZW9tX2JveHBsb3QoZmlsbD0iIzlFREFFNSIpICsNCiMgICAgIHNjYWxlX3lfY29udGludW91cyh0cmFucz1zY2FsZXM6OnBzZXVkb19sb2dfdHJhbnMoYmFzZSA9IDEwKSkgKw0KIyAgICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygxOjEwKSkgKw0KIyAgICAgbGFicyh0aXRsZT0iUmF3IGNvdW50cyIsIHg9InNlZ21lbnQiLCB5ID0gIkNvdW50cywgUmF3IikNCiMgDQojIA0KIyB0ZW1wIDwtYXMubWF0cml4KGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbLDE6MTBdLCBlbHQgPSAicV9ub3JtIikpDQojIGxvbmcgPC0gbWVsdCh0ZW1wKQ0KIyBjb2xuYW1lcyhsb25nKSA8LSBjKCJnZW5lIiwic2VnbWVudCIsImNvdW50IikNCiMgZ2dwbG90KGxvbmcsIGFlcyh4PXNlZ21lbnQseT1jb3VudCkpICsNCiMgICAgIGdlb21fYm94cGxvdChmaWxsID0gIiMyQ0EwMkMiKSArDQojICAgICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnM9c2NhbGVzOjpwc2V1ZG9fbG9nX3RyYW5zKGJhc2UgPSAxMCkpICsNCiMgICAgIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzPWMoMToxMCkpICsNCiMgICAgIGxhYnModGl0bGU9IlEzIE5vcm0gQ291bnRzIiwgeD0ic2VnbWVudCIsIHkgPSAiQ291bnRzLCBRMyBOb3JtYWxpemVkIikNCiMgDQojIA0KIyB0ZW1wIDwtYXMubWF0cml4KGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbLDE6MTBdLCBlbHQgPSAibmVnX25vcm0iKSkNCiMgbG9uZyA8LSBtZWx0KHRlbXApDQojIGNvbG5hbWVzKGxvbmcpIDwtIGMoImdlbmUiLCJzZWdtZW50IiwiY291bnQiKQ0KIyBnZ3Bsb3QobG9uZywgYWVzKHg9c2VnbWVudCx5PWNvdW50KSkgKw0KIyAgICAgZ2VvbV9ib3hwbG90KGZpbGwgPSAiI0ZGN0YwRSIpICsNCiMgICAgIHNjYWxlX3lfY29udGludW91cyh0cmFucz1zY2FsZXM6OnBzZXVkb19sb2dfdHJhbnMoYmFzZSA9IDEwKSkgKw0KIyAgICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHM9YygxOjEwKSkgKw0KIyAgICAgbGFicyh0aXRsZT0iTmVnIE5vcm0gQ291bnRzIiwgeD0ic2VnbWVudCIsIHkgPSAiQ291bnRzLCBOZWcuIE5vcm1hbGl6ZWQiKQ0KYGBgDQoNCiMjIHJhdyBjb3VudHMNCg0KYGBge3J9DQpib3hwbG90KGV4cHJzKHRhcmdldF9EYXRhKVssMTo4XSwNCiAgICAgICAgY29sID0gIiM5RURBRTUiLCBtYWluID0gIlJhdyBDb3VudHMiLA0KICAgICAgICBsb2cgPSAieSIsIG5hbWVzID0gMTo4LCB4bGFiID0gIlNlZ21lbnQiLA0KICAgICAgICB5bGFiID0gIkNvdW50cywgUmF3IikNCmBgYA0KDQojIyBRMyBub3JtYWxpemVkIHsuYWN0aXZlfQ0KDQpRMyAoM3JkIHF1YXJ0aWxlIG9mIGFsbCBzZWxlY3RlZCB0YXJnZXRzKSBpcyB0aGUgcmVjb21tZW5kZWQgbm9ybWFsaXphdGlvbiBtZXRob2QgZm9yIE5HUyBkYXRhIGZvciBhbGwgdGFyZ2V0cyB0aGF0IGFyZSBhYm92ZSB0aGUgbGltaXQgb2YgcXVhbnRpdGF0aW9uIChMT1EpLiBRMyBub3JtYWxpemF0aW9uIHVzZXMgdGhlIHRvcCAyNSUgb2YgZXhwcmVzc2VycyB0byBub3JtYWxpemUgYWNyb3NzIFJPSXMvc2VnbWVudHMsIHNvIGl0IGlzIHJvYnVzdCB0byBjaGFuZ2VzIGluIGV4cHJlc3Npb24gb2YgaW5kaXZpZHVhbCBnZW5lcyBhbmQgaWRlYWwgZm9yIG1ha2luZyBjb21wYXJpc29ucyBhY3Jvc3MgUk9Jcy9zZWdtZW50cy4NCg0KYGBge3J9DQpib3hwbG90KGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbLDE6OF0sIGVsdCA9ICJxX25vcm0iKSwNCiAgICAgICAgY29sID0gIiMyQ0EwMkMiLCBtYWluID0gIlEzIE5vcm0gQ291bnRzIiwNCiAgICAgICAgbG9nID0gInkiLCBuYW1lcyA9IDE6OCwgeGxhYiA9ICJTZWdtZW50IiwNCiAgICAgICAgeWxhYiA9ICJDb3VudHMsIFEzIE5vcm1hbGl6ZWQiKQ0KYGBgDQoNCiMjIFF1YW50aWxlIG5vcm1hbGl6ZWQgKHRlc3RpbmcgcGhhc2UpDQoNClRoZSBxdWFudGlsZSBub3JtYWxpemF0aW9uIGludm9sdmVzIGZpcnN0IHJhbmtpbmcgdGhlIGdlbmUgb2YgZWFjaCBzYW1wbGUgYnkgbWFnbml0dWRlLCBjYWxjdWxhdGluZyB0aGUgYXZlcmFnZSB2YWx1ZSBmb3IgZ2VuZXMgb2NjdXB5aW5nIHRoZSBzYW1lIHJhbmssIGFuZCB0aGVuIHN1YnN0aXR1dGluZyB0aGUgdmFsdWVzIG9mIGFsbCBnZW5lcyBvY2N1cHlpbmcgdGhhdCBwYXJ0aWN1bGFyIHJhbmsgd2l0aCB0aGlzIGF2ZXJhZ2UgdmFsdWUuIFRoZSBuZXh0IHN0ZXAgaXMgdG8gcmVvcmRlciB0aGUgZ2VuZXMgb2YgZWFjaCBzYW1wbGUgaW4gdGhlaXIgb3JpZ2luYWwgb3JkZXJbMl0uDQoNCmBgYHtyfQ0KYm94cGxvdChhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhWywxOjhdLCBlbHQgPSAicXVhbnRpbGVfbm9ybSIpLA0KICAgICAgICBjb2wgPSAicGluayIsIG1haW4gPSAiUXVhbnRpbGUgTm9ybSBDb3VudHMiLA0KICAgICAgICBsb2cgPSAieSIsIG5hbWVzID0gMTo4LCB4bGFiID0gIlNlZ21lbnQiLA0KICAgICAgICB5bGFiID0gIkNvdW50cywgUXVhbnRpbGUgTm9ybWFsaXplZCIpDQpgYGANCg0KIyMgTmVnYXRpdmUgcHJvYmUgbm9ybWFsaXphdGlvbg0KDQpGb3IgZWFjaCBzYW1wbGUsIGFuIFJOQS1wcm9iZS1wb29sLXNwZWNpZmljIG5lZ2F0aXZlIHByb2JlIG5vcm1hbGl6YXRpb24gZmFjdG9yIHdhcyBnZW5lcmF0ZWQgb24gdGhlIGJhc2lzIG9mIHRoZSBnZW9tZXRyaWMgbWVhbiBvZiBuZWdhdGl2ZSBwcm9iZXMgaW4gZWFjaCBwb29sLg0KDQpgYGB7cn0NCmJveHBsb3QoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVssMTo4XSwgZWx0ID0gIm5lZ19ub3JtIiksDQogICAgICAgIGNvbCA9ICIjRkY3RjBFIiwgbWFpbiA9ICJOZWcgTm9ybSBDb3VudHMiLA0KICAgICAgICBsb2cgPSAieSIsIG5hbWVzID0gMTo4LCB4bGFiID0gIlNlZ21lbnQiLA0KICAgICAgICB5bGFiID0gIkNvdW50cywgTmVnLiBOb3JtYWxpemVkIikNCg0KYGBgDQoNCiMgNiBVbnN1cGVydmlzZWQgQW5hbHlzaXMNCg0KIyA2LjEgVU1BUCB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQpVc2UgdGhlIHRhYi1tZW51IHRvIG5hdmlnYXRlIQ0KDQojIyAxDQoNCmBgYHtyIHVtYXAsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTh9DQpjdXN0b21fdW1hcCA8LSB1bWFwOjp1bWFwLmRlZmF1bHRzDQpjdXN0b21fdW1hcCRyYW5kb21fc3RhdGUgPC0gNDINCiMgcnVuIFVNQVANCg0KdW1hcF9vdXQgPC0NCiAgdW1hcCh0KGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSAsIGVsdCA9ICJxX25vcm0iKSkpLA0KICAgICAgIGNvbmZpZyA9IGN1c3RvbV91bWFwKQ0KcERhdGEodGFyZ2V0X0RhdGEpWywgYygiVU1BUDEiLCAiVU1BUDIiKV0gPC0gdW1hcF9vdXQkbGF5b3V0WywgYygxLDIpXQ0KZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gc2xpZGVfbmFtZSwgc2hhcGUgPSBBTk4xKSkgKw0KDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCiMjIDINCg0KYGBge3IgdW1hcDIsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTh9DQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgY29sb3IgPSBBTk4zLCBzaGFwZSA9IEFOTjEpKSArDQoNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICAjZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1yb3cubmFtZXMocERhdGEodGFyZ2V0X0RhdGEpKSksIHNpemU9MixtYXgub3ZlcmxhcHMgPSAxMDApKw0KICB0aGVtZV9idygpDQpgYGANCg0KIyA2LjIgUnVuIHRTTkUgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KVXNlIHRoZSB0YWItbWVudSB0byBuYXZpZ2F0ZSENCg0KT25lIGNvbW1vbiBhcHByb2FjaCB0byB1bmRlcnN0YW5kaW5nIGhpZ2gtcGxleCBkYXRhIGlzIGRpbWVuc2lvbg0KcmVkdWN0aW9uLiBUd28gY29tbW9uIG1ldGhvZHMgYXJlIFVNQVAgYW5kIHRTTkUsIHdoaWNoIGFyZQ0Kbm9uLW9ydGhvZ29uYWxseSBjb25zdHJhaW5lZCBwcm9qZWN0aW9ucyB0aGF0IGNsdXN0ZXIgc2FtcGxlcyBiYXNlZCBvbg0Kb3ZlcmFsbCBnZW5lIGV4cHJlc3Npb24uIEluIHRoaXMgc3R1ZHksIHdlIHNlZSBieSBlaXRoZXIgVU1BUCAoZnJvbSB0aGUNCnVtYXAgcGFja2FnZSkgb3IgdFNORSAoZnJvbSB0aGUgUnRzbmUgcGFja2FnZSkNCg0KIyMgMQ0KDQpgYGB7ciB0U05FLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD04fQ0KdHNuZV9vdXQgPC0NCiAgUnRzbmUodChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEgLCBlbHQgPSAicV9ub3JtIikpKSwNCiAgICAgICAgcGVycGxleGl0eSA9IG5jb2wodGFyZ2V0X0RhdGEpKi4xNSkNCnBEYXRhKHRhcmdldF9EYXRhKVssIGMoInRTTkUxIiwgInRTTkUyIildIDwtIHRzbmVfb3V0JFlbLCBjKDEsMildDQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gdFNORTEsIHkgPSB0U05FMiwgc2hhcGUgPSBzbGlkZV9uYW1lLCBjb2xvciA9IEFOTjIpKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsNCiAgI2dlb21fdGV4dF9yZXBlbChhZXMobGFiZWw9cm93Lm5hbWVzKHBEYXRhKHRhcmdldF9EYXRhKSkpLCBzaXplPTIsbWF4Lm92ZXJsYXBzID0gMTAwKSsNCiAgdGhlbWVfYncoKQ0KYGBgDQoNCiMjIDINCg0KYGBge3IsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTh9DQp0c25lX291dCA8LQ0KICBSdHNuZSh0KGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSAsIGVsdCA9ICJxX25vcm0iKSkpLA0KICAgICAgICBwZXJwbGV4aXR5ID0gbmNvbCh0YXJnZXRfRGF0YSkqLjE1KQ0KcERhdGEodGFyZ2V0X0RhdGEpWywgYygidFNORTEiLCAidFNORTIiKV0gPC0gdHNuZV9vdXQkWVssIGMoMSwyKV0NCmdncGxvdChwRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgYWVzKHggPSB0U05FMSwgeSA9IHRTTkUyLCBjb2xvciA9IEFOTjMsIHNoYXBlID0gQU5OMSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICAjZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1yb3cubmFtZXMocERhdGEodGFyZ2V0X0RhdGEpKSksIHNpemU9MixtYXgub3ZlcmxhcHMgPSAxMDApKw0KICB0aGVtZV9idygpDQpgYGANCg0KSWYgbm8gYmlhcyBpcyBvYnNlcnZlZCBzZXQgYmF0Y2ggY29ycmVjdGlvbiBuZWVkZWQgb24gRkFMU0UsIG90aGVyd2lzZSBUUlVFLg0KDQpgYGB7ciBjaGVja19iYXRjaF9jb3JyZWN0aW9ufQ0KYmF0Y2hfY29ycmVjdGlvbl9uZWVkZWQgPSBGQUxTRQ0KYGBgDQoNCiMgNi4zIEJhdGNoIGNvcnJlY3Rpb24gey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KSWYgdGhlIGRhdGEgaXMgb2JzZXJ2ZWQgdG8gaGF2ZSBhIGJhdGNoIGVmZmVjdCBpdCBjYW4gYmUgY29ycmVjdGVkIHdpdGggdGhlIG1ldGhvZHM6IFJVVjQsIExJTU1BLCBvciBDb21iYXQtU2VxLiBFYWNoIG1ldGhvZCBpcyBkb25lIGFuZCB0aGUgYmVzdCBvbmUgaXMgcGlja2VkIGFuZCB1c2VkLg0KDQpUaGUgc3RhbmRSIHBhY2thZ2UgaXMgc2hvcnQgZm9yIFNwYXRpYWwgdHJhbnNjcmlwdG9taWNzIGFuYWx5emVzIGFuZCBkZWNvZGluZyBpbiBSLCBpdCBhaW1zIGF0IHByb3ZpZGluZyBnb29kIHByYWN0aWNlIHBpcGVsaW5lIGFuZCB1c2VmdWwgZnVuY3Rpb25zIGZvciB1c2VycyB0byBhbmFseXplIE5hbm9zdHJpbmfigJlzIEdlb014IERTUCBkYXRhLiBJbiB0aGUgTmFub3N0cmluZ+KAmXMgR2VvTVggRFNQIHByb3RvY29sLCBkdWUgdG8gdGhlIGZhY3QgdGhhdCBvbmUgc2xpZGUgaXMgb25seSBiaWcgZW5vdWdoIGZvciBhIGhhbmRmdWwgb2YgdGlzc3VlIHNlZ21lbnRzIChST0lzKSwgaXQgaXMgY29tbW9uIHRoYXQgd2Ugc2VlIHRoZSBEU1AgZGF0YSBiZWluZyBjb25mb3VuZGVkIGJ5IHRoZSBiYXRjaCBlZmZlY3QgaW50cm9kdWNlZCBieSBkaWZmZXJlbnQgc2xpZGVzLiBJbiBvcmRlciB0byBlc3RhYmxpc2ggZmFpciBjb21wYXJpc29uIGJldHdlZW4gUk9JcyBsYXRlciBvbiwgaXQgaXMgbmVjZXNzYXJ5IHRvIHJlbW92ZSB0aGlzIGJhdGNoIGVmZmVjdCBmcm9tIHRoZSBkYXRhLiAoaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvc3RhbmRSL2luc3QvZG9jL3N0YW5kUl9pbnRyb2R1Y3Rpb24uaHRtbCkNCg0KRm9yIFJVVjQgY29ycmVjdGlvbiwgdGhlIGZ1bmN0aW9uIGlzIHJlcXVpcmluZyAzIHBhcmFtZXRlcnMgb3RoZXIgdGhhbiB0aGUgaW5wdXQgb2JqZWN0LCBpbmNsdWRpbmcgZmFjdG9yczogdGhlIGZhY3RvciBvZiBpbnRlcmVzdCwgaS5lLiB0aGUgYmlvbG9naWNhbCB2YXJpYXRpb24gd2UgcGxhbiB0byBrZWVwOyBOQ0dzOiB0aGUgbGlzdCBvZiBuZWdhdGl2ZSBjb250cm9sIGdlbmVzIGRldGVjdGVkIHVzaW5nIHRoZSBmdW5jdGlvbiBmaW5kTkNHczsgYW5kIGs6IGlzIHRoZSBudW1iZXIgb2YgdW53YW50ZWQgZmFjdG9ycyB0byB1c2UsIGluIHRoZSBSVVYgZG9jdW1lbnRhdGlvbiwgaXQgaXMgc3VnZ2VzdCB0aGF0IHdlIHNob3VsZCB1c2UgdGhlIHNtYWxsZXN0IGsgb25jZSB3ZSBkb27igJl0IG9ic2VydmUgdGVjaG5pY2FsIHZhcmlhdGlvbiBpbiB0aGUgZGF0YS4NCg0KQW5vdGhlciBvcHRpb24gaXMgc2V0IHRoZSBwYXJhbWV0ZXIgbWV0aG9kIHRvIOKAnExpbW1h4oCdLCB3aGljaCB1c2VzIHRoZSByZW1vdmUgYmF0Y2ggY29ycmVjdGlvbiBtZXRob2QgZnJvbSBsaW1tYS4gSW4gdGhpcyBtb2RlLCB0aGUgZnVuY3Rpb24gaXMgcmVxdWlyaW5nIDIgcGFyYW1ldGVycywgaW5jbHVkaW5nIGJhdGNoOiBhIHZlY3RvciB0aGF0IGluZGljYXRpbmcgYmF0Y2hlcyBmb3IgYWxsIHNhbXBsZXM7IGFuZCBkZXNpZ246IGEgZGVzaWduIG1hdHJpeCB3aGljaCBpcyBnZW5lcmF0ZWQgYnkgbW9kZWwubWF0cml4LCBpbiB0aGUgZGVzaWduIG1hdHJpeCwgYWxsIGJpb2xvZ2ljYWxseS1yZWxldmFudCBmYWN0b3JzIHNob3VsZCBiZSBpbmNsdWRlZC4NCg0KQ29tQmF0LXNlcSBpcyBhIGJhdGNoIGVmZmVjdCBhZGp1c3RtZW50IHRvb2wgZm9yIGJ1bGsgUk5BLXNlcSBjb3VudCBkYXRhLiBJdCBpcyBhbiBpbXByb3ZlZCBtb2RlbCBiYXNlZCBvbiB0aGUgcG9wdWxhciBDb21CYXQsIHRvIGFkZHJlc3MgaXRzIGxpbWl0YXRpb25zIHRocm91Z2ggbm92ZWwgbWV0aG9kcyBkZXNpZ25lZCBzcGVjaWZpY2FsbHkgZm9yIFJOQS1TZXEgc3R1ZGllcy4gQ29tQmF0LXNlcSB0YWtlcyB1bnRyYW5zZm9ybWVkLCByYXcgY291bnQgbWF0cml4IGFzIGlucHV0LiBTYW1lIGFzIENvbUJhdCwgaXQgcmVxdWlyZXMgYSBrbm93biBiYXRjaCB2YXJpYWJsZS4gKGh0dHBzOi8vZ2l0aHViLmNvbS96aGFuZ3l1cWluZy9Db21CYXQtc2VxKQ0KDQojIyBTZXQgdXAgc3RhbmRSIG9iamVjdA0KDQpgYGB7ciBiYXRjaCBjb3JyZWN0aW9uLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0KY291bnRfZ2VvbXggPC0gYXMuZGF0YS5mcmFtZSh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJleHBycyJdXSkNCg0Kc2FtcGxlX2dlb214IDwtIHRhcmdldF9EYXRhQHBoZW5vRGF0YUBkYXRhDQpzYW1wbGVfZ2VvbXgkcm9pIDwtIHRhcmdldF9EYXRhQHByb3RvY29sRGF0YUBkYXRhJHJvaQ0Kc2FtcGxlX2dlb214IDwtIGFzLmRhdGEuZnJhbWUoc2FtcGxlX2dlb214KQ0Kc2FtcGxlX2dlb214JFNhbXBsZV9JRCA8LSByb3duYW1lcyhzYW1wbGVfZ2VvbXgpDQpzYW1wbGVfZ2VvbXgkU2VnbWVudERpc3BsYXlOYW1lIDwtIHBhc3RlKHNhbXBsZV9nZW9teCRgc2NhbiBuYW1lYCwgc2FtcGxlX2dlb214JHJvaSwgc2FtcGxlX2dlb214JHNlZ21lbnQsIHNlcCA9ICIgfCAiKQ0Kc2FtcGxlX2dlb214JFJPSUNvb3JkaW5hdGVYIDwtIDENCnNhbXBsZV9nZW9teCRST0lDb29yZGluYXRlWSA8LSAxDQoNCg0KZmVhdHVyZV9nZW9teCA8LSBmRGF0YShEYXRhKQ0KZmVhdHVyZV9nZW9teCA8LSBmZWF0dXJlX2dlb214W2MoIlJUU19JRCIsICJUYXJnZXROYW1lIiwgIlByb2JlSUQiLCAiTmVnYXRpdmUiKV0NCmZlYXR1cmVfZ2VvbXggPC0gYXMuZGF0YS5mcmFtZShmZWF0dXJlX2dlb214KQ0Kcm93bmFtZXMoZmVhdHVyZV9nZW9teCkgPC0gTlVMTA0KDQptYXRjaGluZyA8LSBzYW1wbGVfZ2VvbXgkU2VnbWVudERpc3BsYXlOYW1lW3NhbXBsZV9nZW9teCRTYW1wbGVfSUQgJWluJSBjb2xuYW1lcyhjb3VudF9nZW9teCldDQpjb2xuYW1lcyhjb3VudF9nZW9teCkgPC0gbWF0Y2hpbmcNCmNvdW50X2dlb214JFRhcmdldE5hbWUgPC0gcm93bmFtZXMoY291bnRfZ2VvbXgpDQpyb3duYW1lcyhjb3VudF9nZW9teCkgPC0gTlVMTA0KDQpzcGUgPC0gcmVhZEdlb014KGNvdW50X2dlb214LCBzYW1wbGVfZ2VvbXgsIGZlYXR1cmVBbm5vRmlsZSA9IGZlYXR1cmVfZ2VvbXgsIGhhc05lZ1Byb2JlID0gVFJVRSkNCg0KY29sRGF0YShzcGUpJHJlZ2lvbnMgPC0gcGFzdGUwKGNvbERhdGEoc3BlKSRBTk4yLCJfIixjb2xEYXRhKHNwZSkkQU5OMSkgfD4gDQogIChcKC4pIGdzdWIoIl9HZW9tZXRyaWMgU2VnbWVudCIsIiIsLikpKCkgfD4NCiAgcGFzdGUwKCJfIixjb2xEYXRhKHNwZSkkcGF0aG9sb2d5KSB8Pg0KICAoXCguKSBnc3ViKCJfTkEiLCJfbnMiLC4pKSgpDQoNCmNvbERhdGEoc3BlKSRyZWdpb25zIDwtIHBhc3RlMChjb2xEYXRhKHNwZSkkQU5OMiwiXyIsY29sRGF0YShzcGUpJEFOTjEpIHw+IA0KICAoXCguKSBnc3ViKCJfR2VvbWV0cmljIFNlZ21lbnQiLCIiLC4pKSgpIHw+DQogIHBhc3RlMCgiXyIsY29sRGF0YShzcGUpJHBhdGhvbG9neSkgfD4NCiAgKFwoLikgZ3N1YigiX05BIiwiX25zIiwuKSkoKQ0KY29sRGF0YShzcGUpJGJpb2xvZ3kgPC0gcGFzdGUwKGNvbERhdGEoc3BlKSRyZWdpb25zKQ0KYGBgDQoNCiMjIFNlZSBvcHRpbWFsIGsgdmFsdWUgZm9yIFJVVjQNCg0KYGBge3IsIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQpzcGUgPC0gZmluZE5DR3Moc3BlLCBiYXRjaF9uYW1lID0gInNsaWRlX25hbWUiLCB0b3BfbiA9IDUwMCkNCmZpbmRCZXN0SyhzcGUsIG1heEsgPSAxMCwgZmFjdG9yX29mX2ludCA9ICJiaW9sb2d5IiwgTkNHcyA9IG1ldGFkYXRhKHNwZSkkTkNHcywgZmFjdG9yX2JhdGNoID0gInNsaWRlX25hbWUiKQ0KYGBgDQoNCiMjIFJVVjQNCg0KYGBge3IgcnV2NCBiYXRjaCwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9MTAsIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQpydXY0IDwtIGdlb214QmF0Y2hDb3JyZWN0aW9uKHNwZSwgZmFjdG9ycyA9ICJiaW9sb2d5IiwgDQogICAgICAgICAgICAgICAgICAgTkNHcyA9IG1ldGFkYXRhKHNwZSkkTkNHcywgayA9IDEpDQoNCnBsb3RQYWlyUENBKHJ1djQsIGFzc2F5ID0gMiwgY29sb3IgPSBzbGlkZV9uYW1lLCBzaGFwZSA9IHJlZ2lvbnMsIHRpdGxlID0gIlJVVjQgcmVtb3ZlQmF0Y2giKQ0KDQpwbG90UkxFeHByKHJ1djQsIGFzc2F5ID0gMiwgY29sb3IgPSBgc2xpZGVfbmFtZWApICsgZ2d0aXRsZSgiUlVWNCByZW1vdmVCYXRjaCIpDQpgYGANCg0KIyMgTGltbWENCg0KYGBge3IgbGltbWEgYmF0Y2gsICBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD0xMCwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCmxpbW1hIDwtIGdlb214QmF0Y2hDb3JyZWN0aW9uKHNwZSwNCiAgICAgICAgICAgICAgICAgICAgICAgYmF0Y2ggPSBjb2xEYXRhKHNwZSkkYHNsaWRlX25hbWVgLCBtZXRob2QgPSAiTGltbWEiLA0KICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSBtb2RlbC5tYXRyaXgofiAwICsgQU5OMSArIHJlZ2lvbnMsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGNvbERhdGEoc3BlKSkpDQoNCnBsb3RQYWlyUENBKGxpbW1hLCBhc3NheSA9IDIsIGNvbG9yID0gc2xpZGVfbmFtZSwgc2hhcGUgPSByZWdpb25zLCB0aXRsZSA9ICJMaW1tYSByZW1vdmVCYXRjaCIpDQoNCnBsb3RSTEV4cHIobGltbWEsIGFzc2F5ID0gMiwgY29sb3IgPSBzbGlkZV9uYW1lKSArIGdndGl0bGUoIkxpbW1hIHJlbW92ZUJhdGNoIikNCmBgYA0KDQojIyBDb21iYXRTZXENCg0KYGBge3IsIGV2YWw9YmF0Y2hfY29ycmVjdGlvbl9uZWVkZWR9DQphZGp1c3RlZCA8LSBDb21CYXRfc2VxKHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dLCBiYXRjaD10YXJnZXRfRGF0YUBwaGVub0RhdGFAZGF0YVtbInNsaWRlX25hbWUiXV0sIGdyb3VwPXRhcmdldF9EYXRhQHBoZW5vRGF0YUBkYXRhW1siQU5OMiJdXSkNCmFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJjb21iYXQiKSA8LSAgYWRqdXN0ZWQNCg0KdW1hcF9vdXQyIDwtDQogIHVtYXAodChsb2cyKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEgLCBlbHQgPSAiY29tYmF0IikpKSwNCiAgICAgICBjb25maWcgPSBjdXN0b21fdW1hcCkNCnBEYXRhKHRhcmdldF9EYXRhKVssIGMoIlVNQVAxIiwgIlVNQVAyIildIDwtIHVtYXBfb3V0MiRsYXlvdXRbLCBjKDEsMildDQpnZ3Bsb3QocERhdGEodGFyZ2V0X0RhdGEpLA0KICAgICAgIGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgY29sb3IgPSBzbGlkZV9uYW1lLCBzaGFwZSA9IEFOTjEpKSArDQoNCiAgZ2VvbV9wb2ludChzaXplID0gMykgKw0KICAjZ2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbD1yb3cubmFtZXMocERhdGEodGFyZ2V0X0RhdGEpKSksIHNpemU9MixtYXgub3ZlcmxhcHMgPSAxMDApKw0KICB0aGVtZV9idygpICsgZ2d0aXRsZSgiQ29tYmF0U2VxIHJlbW92ZUJhdGNoIikNCmBgYA0KDQojIyBDb21wYXJlIGxpbW1hIHZzIFJVVjQNCg0KYGBge3IgY29tcGFyZSBiYXRjaCwgZXZhbD1iYXRjaF9jb3JyZWN0aW9uX25lZWRlZH0NCnNwZV9saXN0IDwtIGxpc3Qoc3BlLCBydXY0LCBsaW1tYSkNCg0KcGxvdENsdXN0ZXJFdmFsU3RhdHMoc3BlX2xpc3QgPSBzcGVfbGlzdCwNCiAgICAgICAgICAgICAgICAgICAgIGJpb19mZWF0dXJlX25hbWUgPSAicmVnaW9ucyIsDQogICAgICAgICAgICAgICAgICAgICBiYXRjaF9mZWF0dXJlX25hbWUgPSAic2xpZGVfbmFtZSIsDQogICAgICAgICAgICAgICAgICAgICBkYXRhX25hbWVzID0gYygiUmF3IiwiUlVWNCIsIkxpbW1hIikpDQpgYGANCg0KIyMgQWRkIGJhdGNoIGNvcnJlY3Rpb24gcmVzdWx0IHRvIHRhcmdldF9EYXRhDQoNCmBgYHtyIGFkZCBiYXRjaCBjb3JyZWN0aW9uLCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0KbmVnX3Byb2Jlc19zYXZlIDwtIHQoYXMubWF0cml4KHRhcmdldF9EYXRhQGFzc2F5RGF0YSRxX25vcm1bIk5lZ1Byb2JlLVdUWCIsXSkpDQpyb3duYW1lcyhuZWdfcHJvYmVzX3NhdmUpIDwtICJOZWdQcm9iZS1XVFgiDQoNCiMgRGVwZW5kaW5nIG9uIGNvcnJlY3QgbWV0aG9kLCBjaGFuZ2UgdGhlIHdvcmQgImxpbW1hIiB0byAicnV2NCIgb3IgdmljZSB2ZXJzYS4NCmxpbW1hIDwtbGltbWFAYXNzYXlzQGRhdGFAbGlzdERhdGFbWyJsb2djb3VudHMiXV0NCmxpbW1hIDwtIHJiaW5kKGxpbW1hLCBuZWdfcHJvYmVzX3NhdmUpDQpjb2xuYW1lcyhsaW1tYSkgPC0gY29sbmFtZXMoYXMuZGF0YS5mcmFtZSh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJxX25vcm0iXV0pKQ0KYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gImxpbW1hIikgPC0gIGxpbW1hDQoNCnJ1djQgPC1ydXY0QGFzc2F5c0BkYXRhQGxpc3REYXRhW1sibG9nY291bnRzIl1dDQpydXY0IDwtIHJiaW5kKHJ1djQsIG5lZ19wcm9iZXNfc2F2ZSkNCmNvbG5hbWVzKHJ1djQpIDwtIGNvbG5hbWVzKGFzLmRhdGEuZnJhbWUodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sicV9ub3JtIl1dKSkNCmFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJydXY0IikgPC0gIHJ1djQNCmBgYA0KDQojIyBDaG9vc2UgbWV0aG9kIHsuYWN0aXZlfQ0KDQpgYGB7cn0NCiMgY2hvb3NlIG1ldGhvZCB0byByZXBsYWNlIHFfbm9ybSBvciBzZXQgaXQgdG8gIiINCm1ldGhvZCA8LSAiIg0KDQojIHJlcGxhY2UgcV9ub3JtIHdpdGggY2hvc2VuIG1ldGhvZA0KaWYgKCFtZXRob2QgPT0gIiIpIHsNCiAgIyBzYXZlIG9yZ2luYWwgcV9ub3JtDQogIGFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJvcmlnaW5hbCIpIDwtICBhc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAicV9ub3JtIikNCiAgDQogIGFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJxX25vcm0iKSA8LSAgYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gbWV0aG9kKQ0KICBwcmludChwYXN0ZSgiQmF0Y2ggY29ycmVjdGlvbiBtZXRob2Q6IiwgbWV0aG9kKSkNCn0gZWxzZSB7cHJpbnQoIk5vIGJhdGNoIGNvcnJlY3Rpb24gbmVlZGVkIil9DQpgYGANCg0KIyMgVW1hcCBhZnRlciBiYXRjaCBjb3JyZWN0aW9uDQoNCmBgYHtyLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD04LCBldmFsPWJhdGNoX2NvcnJlY3Rpb25fbmVlZGVkfQ0KdW1hcF9vdXQgPC0NCiAgdW1hcCh0KGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSAsIGVsdCA9ICJxX25vcm0iKSkpLA0KICAgICAgIGNvbmZpZyA9IGN1c3RvbV91bWFwKQ0KcERhdGEodGFyZ2V0X0RhdGEpWywgYygiVU1BUDEiLCAiVU1BUDIiKV0gPC0gdW1hcF9vdXQkbGF5b3V0WywgYygxLDIpXQ0KZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gc2xpZGVfbmFtZSwgc2hhcGUgPSBBTk4xKSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogICNnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsPXJvdy5uYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSkpKSwgc2l6ZT0yLG1heC5vdmVybGFwcyA9IDEwMCkrDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIDYuNCBDbHVzdGVyaW5nIGhpZ2ggQ1YgR2VuZXMgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KQW5vdGhlciBhcHByb2FjaCB0byBleHBsb3JlIHRoZSBkYXRhIGlzIHRvIGNhbGN1bGF0ZSB0aGUgY29lZmZpY2llbnQgb2YNCnZhcmlhdGlvbiAoJENWJCkgZm9yIGVhY2ggZ2VuZSAoJGckKSB1c2luZyB0aGUgZm9ybXVsYQ0KJENWX2c9U0RfZy9tZWFuX2ckLiBXZSB0aGVuIGlkZW50aWZ5IGdlbmVzIHdpdGggaGlnaCBDVnMgdGhhdCBzaG91bGQNCmhhdmUgbGFyZ2UgZGlmZmVyZW5jZXMgYWNyb3NzIHRoZSB2YXJpb3VzIHByb2ZpbGVkIHNlZ21lbnRzLiBUaGlzDQp1bmJpYXNlZCBhcHByb2FjaCBjYW4gcmV2ZWFsIGhpZ2hseSB2YXJpYWJsZSBnZW5lcyBhY3Jvc3MgdGhlIHN0dWR5Lg0KDQpXZSBwbG90IHRoZSByZXN1bHRzIHVzaW5nIHVuc3VwZXJ2aXNlZCBoaWVyYXJjaGljYWwgY2x1c3RlcmluZywNCmRpc3BsYXllZCBhcyBhIGhlYXRtYXAuDQoNCmBgYHtyIGNsdXN0ZXJpbmcxfQ0KIyBjcmVhdGUgYSBsb2cyIHRyYW5zZm9ybSBvZiB0aGUgZGF0YSBmb3IgYW5hbHlzaXMNCmFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJsb2dfcSIpIDwtDQogIGFzc2F5RGF0YUFwcGx5KHRhcmdldF9EYXRhLCAyLCBGVU4gPSBsb2csIGJhc2UgPSAyLCBlbHQgPSAicV9ub3JtIikNCg0KIyBjcmVhdGUgQ1YgZnVuY3Rpb24NCmNhbGNfQ1YgPC0gZnVuY3Rpb24oeCkge3NkKHgpIC8gbWVhbih4KX0NCkNWX2RhdCA8LSBhc3NheURhdGFBcHBseSh0YXJnZXRfRGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBlbHQgPSAibG9nX3EiLCBNQVJHSU4gPSAxLCBjYWxjX0NWKQ0KIyBzaG93IHRoZSBoaWdoZXN0IENEIGdlbmVzIGFuZCB0aGVpciBDViB2YWx1ZXMNCnNvcnQoQ1ZfZGF0LCBkZWNyZWFzaW5nID0gVFJVRSlbMTo1XQ0KYGBgDQoNCiMjIFRhYmxlIG9mIENWIHZhbHVlcw0KDQpgYGB7cn0NCiMgc2hvdyB0aGUgaGlnaGVzdCBDRCBnZW5lcyBhbmQgdGhlaXIgQ1YgdmFsdWVzDQpkYXRhdGFibGUoYXMuZGF0YS5mcmFtZShDVl9kYXQpLA0KICAgICAgICAgIGV4dGVuc2lvbnMgPSAnQnV0dG9ucycsIG9wdGlvbnMgPSBsaXN0ICgNCiAgICAgICAgICAgIG9yZGVyID0gbGlzdCgxLCAnZGVzYycpLA0KICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICApLCBjYXB0aW9uID0gIkNWIHZhbHVlcyBvZiBnZW5lcyIgDQopICU+JSBmb3JtYXRSb3VuZChjb2x1bW5zPWMoIkNWX2RhdCIpLCBkaWdpdHM9MykNCg0KYGBgDQoNCiMjIEhlYXRtYXAgZ2VuZXMgaW4gdGhlIHRvcCAzcmQgb2YgdGhlIENWIHZhbHVlcyB7LmFjdGl2ZX0NCg0KRm9yIGhlYXRtYXAgY2x1c3RlcmluZyBtZXRob2RzIHNlZTogDQpHZW5lcmFsIGhlYXRtYXAgaW5mbzoNCmh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9waGVhdG1hcC92ZXJzaW9ucy8xLjAuMTIvdG9waWNzL3BoZWF0bWFwDQpEaXN0YW5jZSBtZXRob2RzIGZ1cnRoZXIgZXhwbGFpbmVkOg0KaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3N0YXRzL3ZlcnNpb25zLzMuNi4yL3RvcGljcy9kaXN0DQpjbHVzdGVyaW5nIG1ldGhvZHMgZnVydGhlciBleHBsYWluZWQ6DQpodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvc3RhdHMvdmVyc2lvbnMvMy42LjIvdG9waWNzL2hjbHVzdA0KDQpgYGB7ciBDVmhlYXRtYXAsIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTE1fQ0KIyBjaG9vc2UgY2x1c3RlcmluZyBtZXRob2RzIGZvciBoZWF0bWFwcw0KY2x1c3RfbWV0aG9kIDwtICJhdmVyYWdlIiAgICAjIG9wdGlvbnM6IGF2ZXJhZ2UsIHdhcmQuRCwgd2FyZC5EMiwgc2luZ2xlLCBjb21wbGV0ZSwgbWNxdWl0dHksIG1lZGlhbiBhbmQgY2VudHJvaWQuDQojIGNob29zZSBjbHVzdGVyaW5nIGRpc3RhbmNlIG1ldGhvZHMgZm9yIGhlYXRtYXBzDQpkaXN0X21ldGhvZCA8LSAiY29ycmVsYXRpb24iICMgb3B0aW9uczogY29ycmVsYXRpb24sIGV1Y2xpZGVhbiwgbWF4aW11bSwgbWFuaGF0dGFuLCBjYW5iZXJyYSwgYmluYXJ5IGFuZCBtaW5rb3dza2kuDQoNCkdPSSA8LSBuYW1lcyhDVl9kYXQpW0NWX2RhdCA+IHF1YW50aWxlKENWX2RhdCwgMC43NSldDQoNCnBoZWF0bWFwKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbR09JLCBdLCBlbHQgPSAibG9nX3EiKSwNCiAgICAgICAgIHNjYWxlID0gInJvdyIsDQogICAgICAgICBjdXRyZWVfY29scyA9IDMsDQogICAgICAgICBjdXRyZWVfcm93cyA9IDIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gRkFMU0UsIHNob3dfY29sbmFtZXMgPSBUUlVFLA0KICAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsDQogICAgICAgICBkcm9wX2xldmVscyA9IFRSVUUsDQogICAgICAgICBjbHVzdGVyaW5nX21ldGhvZCA9IGNsdXN0X21ldGhvZCwNCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9IGRpc3RfbWV0aG9kLA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gZGlzdF9tZXRob2QsDQogICAgICAgICBicmVha3MgPSBzZXEoLTMsIDMsIDAuMDUpLA0KICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoInB1cnBsZTMiLCAiYmxhY2siLCAieWVsbG93MiIpKSgxMjApLA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KDQpgYGANCg0KYGBge3IgbG9nX3RyYW5zZm9ybX0NCmFzc2F5RGF0YUVsZW1lbnQob2JqZWN0ID0gdGFyZ2V0X0RhdGEsIGVsdCA9ICJsb2dfcSIpIDwtICBhc3NheURhdGFBcHBseSh0YXJnZXRfRGF0YSwgMiwgRlVOID0gbG9nLCBiYXNlID0gMiwgZWx0ID0gInFfbm9ybSIpDQpsb2dfcSA8LWFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAibG9nX3EiKSkNCiNiYXRjaCA8LWFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAiYmF0Y2giKSkNCmBgYA0KDQojIDYuNS4wIENyZWF0ZSBzdWJzZXQgb2YgZGF0YQ0KDQpJZiBubyBzdWJzZXQgaXMgbmVlZGVkIHNldCBzdWJzZXRfbmVlZGVkIG9uIEZBTFNFLCBvdGhlcndpc2UgVFJVRS4NCg0KYGBge3IgZGVmaW5lX2FjdGl2ZV9hb2lzfQ0KIyBkZXRlcm1pbmUgQU9JcyB0byB1c2UNCnN1YnNldF9uZWVkZWQgPC0gRkFMU0UNCg0KI2FjdGl2ZV9hb2lzPC1yb3duYW1lcyhhbm4pW2FubiRwYXRpZW50PT0icDQiXQ0KYWN0aXZlX2FvaXM8LSBuYW1lcyhhcy5kYXRhLmZyYW1lKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEsIGVsdD0gImV4cHJzIikpKQ0KYGBgDQoNCiMgNi41LjEgQ2x1c3RlcmluZyBoaWdoIENWIGdlbmVzIGZvciBzdWJzZXQgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KQ2FsY3VsYXRpbmcgQ1YgdmFsdWVzDQoNCmBgYHtyIGNsdXN0ZXJpbmdfc3Vic2V0LCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD0xNX0NCiMgY3JlYXRlIGEgbG9nMiB0cmFuc2Zvcm0gb2YgdGhlIGRhdGEgZm9yIGFuYWx5c2lzDQphc3NheURhdGFFbGVtZW50KG9iamVjdCA9IHRhcmdldF9EYXRhLCBlbHQgPSAibG9nX3EiKSA8LQ0KICBhc3NheURhdGFBcHBseSh0YXJnZXRfRGF0YSwgMiwgRlVOID0gbG9nLCBiYXNlID0gMiwgZWx0ID0gInFfbm9ybSIpDQoNCiMgY3JlYXRlIENWIGZ1bmN0aW9uDQpjYWxjX0NWIDwtIGZ1bmN0aW9uKHgpIHtzZCh4KSAvIG1lYW4oeCl9DQpDVl9kYXQgPC0gYXNzYXlEYXRhQXBwbHkodGFyZ2V0X0RhdGFbLGFjdGl2ZV9hb2lzXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBlbHQgPSAibG9nX3EiLCBNQVJHSU4gPSAxLCBjYWxjX0NWKQ0KDQpgYGANCg0KIyMgVGFibGUgb2YgQ1YgdmFsdWVzDQoNCmBgYHtyfQ0KIyBzaG93IHRoZSBoaWdoZXN0IENEIGdlbmVzIGFuZCB0aGVpciBDViB2YWx1ZXMNCmRhdGF0YWJsZShhcy5kYXRhLmZyYW1lKENWX2RhdCksDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgb3JkZXIgPSBsaXN0KDEsICdkZXNjJyksDQogICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICksIGNhcHRpb24gPSAiQ1YgdmFsdWVzIG9mIGdlbmVzIiANCikgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygiQ1ZfZGF0IiksIGRpZ2l0cz0zKQ0KDQpgYGANCg0KIyMgSGVhdG1hcCBvbiBvZiBzdWJzZXQsIGdlbmVzIGluIHRoZSB0b3AgM3JkIG9mIHRoZSBDViB2YWx1ZXMgey5hY3RpdmV9DQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xNSwgZXZhbD1zdWJzZXRfbmVlZGVkfQ0KIyBjaG9vc2UgY2x1c3RlcmluZyBtZXRob2RzIGZvciBoZWF0bWFwcw0KY2x1c3RfbWV0aG9kIDwtICJhdmVyYWdlIiAgICAjIG9wdGlvbnM6IGF2ZXJhZ2UsIHdhcmQuRCwgd2FyZC5EMiwgc2luZ2xlLCBjb21wbGV0ZSwgbWNxdWl0dHksIG1lZGlhbiBhbmQgY2VudHJvaWQuDQojIGNob29zZSBjbHVzdGVyaW5nIGRpc3RhbmNlIG1ldGhvZHMgZm9yIGhlYXRtYXBzDQpkaXN0X21ldGhvZCA8LSAiY29ycmVsYXRpb24iICMgb3B0aW9uczogY29ycmVsYXRpb24sIGV1Y2xpZGVhbiwgbWF4aW11bSwgbWFuaGF0dGFuLCBjYW5iZXJyYSwgYmluYXJ5IGFuZCBtaW5rb3dza2kuDQoNCiMgSWRlbnRpZnkgZ2VuZXMgaW4gdGhlIHRvcCAzcmQgb2YgdGhlIENWIHZhbHVlcw0KR09JIDwtIG5hbWVzKENWX2RhdClbQ1ZfZGF0ID4gcXVhbnRpbGUoQ1ZfZGF0LCAwLjc1KV0NCnBoZWF0bWFwKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGFbR09JLGFjdGl2ZV9hb2lzIF0sIGVsdCA9ICJsb2dfcSIpLA0KICAgICAgICBzY2FsZSA9ICJyb3ciLA0KICAgICAgICBmb250c2l6ZV9yb3cgPSA1LA0KICAgICAgICBjdXRyZWVfY29scyA9IDMsDQogICAgICAgIGN1dHJlZV9yb3dzID0gMiwNCiAgICAgICAgc2hvd19yb3duYW1lcyA9IEZBTFNFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgYm9yZGVyX2NvbG9yID0gTkEsDQogICAgICAgIGNsdXN0ZXJpbmdfbWV0aG9kID0gY2x1c3RfbWV0aG9kLA0KICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBkaXN0X21ldGhvZCwNCiAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gZGlzdF9tZXRob2QsDQogICAgICAgIGJyZWFrcyA9IHNlcSgtMywgMywgMC4wNSksDQogICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJwdXJwbGUzIiwgImJsYWNrIiwgInllbGxvdzIiKSkoMTIwKSwNCiAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgIGFubm90YXRpb25fY29sID0NCiAgICAgICAgICBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdKQ0KYGBgDQoNCiMgNy4xIERpZmZlcmVudGlhbCBFeHByZXNzaW9uDQoNCiFbXShMOi9wa2xvb3N0ZXJtYW4vS2lkbmV5X29yZ2FuX2F0bGFzL2tpZG5leV9zdHJ1Y3R1cmUucG5nKQ0KDQojIyB0LXRlc3QNCg0KYGBge3IgdHRlc3R9DQojZ2MoKQ0KcGxvdHM8LWxpc3QoKQ0KdGFibGVzPC1saXN0KCkNCmxhYmVsczwtbGlzdCgpDQp0ZXN0PC0idHRlc3QiDQptdGM8LSJCWSINCiNvcHRpb25zOiAiaG9sbSIgICAgICAgImhvY2hiZXJnIiAgICJob21tZWwiICAgICAiYm9uZmVycm9uaSIgIkJIIiAgICAgICAgICJCWSIgICAgICAgICAiZmRyIiANCmNvdW50ZXI9MQ0KDQpjb21wc19kZjwtZGF0YS5mcmFtZShjb21wPScnLHZhbD0nJykNCg0KZm9yKHJlZ2lvbiBpbiB1bmlxdWUocERhdGEodGFyZ2V0X0RhdGEpJEFOTjMpKSB7DQogIGZvciAoYWN0aXZlX2dyb3VwMSBpbiB1bmlxdWUocERhdGEodGFyZ2V0X0RhdGEpJEFOTjEpKSB7DQogICAgZm9yIChhY3RpdmVfZ3JvdXAyIGluIHVuaXF1ZShwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMSkpIHsNCiAgICAgIA0KICAgICAgI3N1cHJlc3MgcmVkdW5jYW50IGNvbXBhcmVzDQogICAgICBpZihhY3RpdmVfZ3JvdXAxPT1hY3RpdmVfZ3JvdXAyKSB7bmV4dH0NCiAgICAgIGNvbXA8LXBhc3RlKHNvcnQoYyhyZWdpb24sIGFjdGl2ZV9ncm91cDEsYWN0aXZlX2dyb3VwMikpLGNvbGxhcHNlID0gIl8iKQ0KICAgICAgaWYoY29tcCAlaW4lIGNvbXBzX2RmJGNvbXApIHtuZXh0fQ0KICAgICAgdGVtcF9kZjwtZGF0YS5mcmFtZShjb21wPWNvbXAgLHZhbD0xKQ0KICAgICAgY29tcHNfZGY8LXJiaW5kKGNvbXBzX2RmLHRlbXBfZGYpDQogICAgICANCiAgICAgIGxhYmVsc1tbY291bnRlcl1dPC1wYXN0ZShhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikNCiAgICAgIGdyb3VwMTwtbG9nX3FbLG5hbWVzKGFzLmRhdGEuZnJhbWUoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YSwgZWx0PSAiZXhwcnMiKSkpW3BEYXRhKHRhcmdldF9EYXRhKSRBTk4xPT1hY3RpdmVfZ3JvdXAxICYgcERhdGEodGFyZ2V0X0RhdGEpJEFOTjM9PXJlZ2lvbl1dDQogICAgICBncm91cDI8LWxvZ19xWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEsIGVsdD0gImV4cHJzIikpKVtwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMT09YWN0aXZlX2dyb3VwMiAmIHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zPT1yZWdpb25dXQ0KICAgICAgDQogICAgICAjcnVuIHRfdGVzdHMgIA0KICAgICAgcmVzdWx0czwtYXMuZGF0YS5mcmFtZSAoIGFwcGx5KGxvZ19xLCAxLCBmdW5jdGlvbih4KSB0LnRlc3QoeFtjb2xuYW1lcyhncm91cDEpXSx4W2NvbG5hbWVzKGdyb3VwMildKSRwLnZhbHVlKSApDQogICAgICBjb2xuYW1lcyhyZXN1bHRzKTwtInJhd19wX3ZhbHVlIg0KICAgICAgDQogICAgICAjbXVsdGlwbGVfdGVzdGluZ19jb3JyZWN0aW9uDQogICAgICBhZGpfcF92YWx1ZTwtIHAuYWRqdXN0KHJlc3VsdHMkcmF3X3BfdmFsdWUsbWV0aG9kPW10YykNCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsYWRqX3BfdmFsdWUpDQogICAgICANCiAgICAgICNjYWxjX2Zkcg0KICAgICAgRkRSPC0gcC5hZGp1c3QocmVzdWx0cyRyYXdfcF92YWx1ZSxtZXRob2Q9ImZkciIpDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLEZEUikNCiAgICAgIA0KICAgICAgI2ZvbGRfY2hhbmdlcw0KICAgICAgI2FzIGJhc2UgZGF0YSBpcyBhbHJlYWR5IGxvZyB0cmFuc2Zvcm1lZCwgbWVhbnMgbmVlZCB0byBiZSBzdWJ0cmFjdGVkIHRvIGdldCBGQyBpbiBsb2cgc3BhY2UNCiAgICAgIGZjaGFuZ2VzPC1hcy5kYXRhLmZyYW1lKGFwcGx5KGxvZ19xLCAxLCBmdW5jdGlvbih4KSAobWVhbih4W2NvbG5hbWVzKGdyb3VwMSldKSAtIG1lYW4oeFtjb2xuYW1lcyhncm91cDIpXSkpKSkNCiAgICAgIGNvbG5hbWVzKGZjaGFuZ2VzKTwtIkZDIg0KICAgICAgI3Bhc3RlKCJGQyIsYWN0aXZlX2dyb3VwMSwiIC8gIixhY3RpdmVfZ3JvdXAyKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxmY2hhbmdlcykNCiAgICAgIA0KICAgICAgI2FkZCBnZW5lbmFtZXMNCiAgICAgIHJlc3VsdHMkR2VuZTwtcm93bmFtZXMocmVzdWx0cykNCiAgICAgIA0KICAgICAgI3NldCBjYXRlZ29yaWVzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQogICAgICByZXN1bHRzJENvbG9yIDwtICJOUyBvciBGQyA8IDEiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkcmF3X3BfdmFsdWUgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQogICAgICByZXN1bHRzJENvbG9yW3Jlc3VsdHMkRkRSIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCiAgICAgIHJlc3VsdHMkQ29sb3JbYWJzKHJlc3VsdHMkRkMpIDwxXSA8LSAiTlMgb3IgRkMgPCAxIg0KICAgICAgcmVzdWx0cyRDb2xvciA8LSBmYWN0b3IocmVzdWx0cyRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMSIsICJQIDwgMC4wNSIsICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQogICAgICANCiAgICAgICN2dWxjYW5vcGxvdA0KICAgICAgDQogICAgICAjIHBpY2sgdG9wIGdlbmVzIGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQogICAgICAjIG9yZGVyIGdlbmVzIGZvciBjb252ZW5pZW5jZToNCiAgICAgIA0KICAgICAgcmVzdWx0cyRpbnZlcnRfUCA8LSAoLWxvZzEwKHJlc3VsdHMkYWRqX3BfdmFsdWUpKSAqIHNpZ24ocmVzdWx0cyRGQykNCiAgICAgIHRvcF9nIDwtIGMoKQ0KICAgICAgdG9wX2cgPC0gYyh0b3BfZywNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1ssICdHZW5lJ11bDQogICAgICAgICAgICAgICAgICAgb3JkZXIocmVzdWx0c1ssICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gVFJVRSlbMToyMF1dLA0KICAgICAgICAgICAgICAgICByZXN1bHRzWywgJ0dlbmUnXVtvcmRlcihyZXN1bHRzWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToyMF1dKQ0KICAgICAgdG9wX2c8LSB1bmlxdWUodG9wX2cpDQogICAgICByZXN1bHRzIDwtIHJlc3VsdHNbLCAtMSpuY29sKHJlc3VsdHMpXSAjIHJlbW92ZSBpbnZlcnRfUCBmcm9tIG1hdHJpeA0KICAgICAgDQogICAgICAjIEdyYXBoIHJlc3VsdHMNCiAgICAgIA0KICAgICAgcGxvdHNbW2NvdW50ZXJdXTwtIGdncGxvdChyZXN1bHRzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IEZDLCB5ID0gLWxvZzEwKHJhd19wX3ZhbHVlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gQ29sb3IsIGxhYmVsID0gR2VuZSkpICsNCiAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygxLCAtMSksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgZ2VvbV9wb2ludCgpICsNCiAgICAgICAgbGFicyh4ID0gcGFzdGUoIkVucmljaGVkIGluIiwgYWN0aXZlX2dyb3VwMiwiIDwtIGxvZzIoRkMpIC0+IEVucmljaGVkIGluIiwgYWN0aXZlX2dyb3VwMSksDQogICAgICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbmNlIikgKw0KICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYFAgPCAwLjA1YCA9ICJvcmFuZ2UyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC41YCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMDUpKSkgKw0KICAgICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChyZXN1bHRzLCAoLTE+RkN8IEZDPjEpICYgRkRSIDwgMC4wNSAmIEdlbmUgJWluJSB0b3BfZyksDQogICAgICAgICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gMC4xNSwgY29sb3IgPSAiYmxhY2siLCBzaXplPTMuNSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5zZWdtZW50Lmxlbmd0aCA9IC4xLCBib3gucGFkZGluZyA9IC4yLCBsd2QgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzID0gNTApICsNCiAgICAgICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTApICsNCiAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICAgICAgZ2d0aXRsZShwYXN0ZSgiY2xhc3M6ICIsIHJlZ2lvbiwiIC0gIiwgdGVzdCwgbXRjLCJtdWx0aXRlc3QgY29yciIpKQ0KICAgICAgDQogICAgICAjc3RvcmUgdGFibGVzIGZvciBkaXNwbGF5IGxhdGVyDQogICAgICB0YWJsZXNbW2NvdW50ZXJdXTwtcmVzdWx0cw0KICAgICAgDQogICAgICBjb3VudGVyID0gY291bnRlcisxDQogICAgICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCiAgICB9DQogIH0NCn0NCmdyaWQuYXJyYW5nZShncm9icz1wbG90cyxuY29sPTIpDQpgYGANCg0KYGBge3IgZHVtcF90YWJsZXMscmVzdWx0cz0nYXNpcyd9DQojc3RyYW5nbHkgZG9lcyBub3QgYXBwZWFyIGluIGh0bWwgb3V0cHV0IC0+IGNodWNrcyBhcmUgbGltaXRlZCB0byBvbmUgZGF0YXRhYmxlIHBlciBjaHVjay4gVGhlIGh0bWx0b29scyBhcmUgYSB3YXkgYXJvdW5kIHRoaXMgYnV0IGRvZXMgc2hvdyB0aGVtIHN0YWNrZWQgaW4gYSBib3guDQpyZXN1bHRzX3RhYmxlcyA8LSBodG1sdG9vbHM6OnRhZ0xpc3QoKQ0KZm9yIChjIGluICgyOmNvdW50ZXItMSkpIHsNCiAgI0dlbmUgJWluJSBHT0kNCiAgcmVzdWx0c190YWJsZXNbW2NdXSA8LSBkYXRhdGFibGUoc3Vic2V0KHRhYmxlc1tbY11dLCBDb2xvciA9PSBjKCJGRFIgPCAwLjAwMSIsICAiUCA8IDAuMDUiKSksIA0KICAgICAgICAgICByb3duYW1lcz1GQUxTRSwNCiAgICAgICAgICAgZXh0ZW5zaW9ucyA9IGMoJ0J1dHRvbnMnKSwgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgICBkb20gPSAnQmZ0cmlwJywNCiAgICAgICAgICAgICAgYnV0dG9ucyA9IGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicsICdwcmludCcpDQogICAgICAgICAgICApLCBoZWlnaHQgPSA1NTAsDQogICAgICAgICAgIGNhcHRpb24gPSBwYXN0ZSgiREUgcmVzdWx0cyAiLCBsYWJlbHNbW2NdXSksZmlsdGVyPSd0b3AnKSAlPiUgZm9ybWF0Um91bmQoY29sdW1ucz1jKCJyYXdfcF92YWx1ZSIsImFkal9wX3ZhbHVlIiwiRkRSIiwiRkMiKSwgZGlnaXRzPTMpDQogICNjYXQoJ1xuXG48IS0tIC0tPlxuXG4nKQ0KfSAgICAgICAgICAgIA0KcmVzdWx0c190YWJsZXMNCmBgYA0KDQpgYGB7cn0NCnByaW50KCIiKSAjIGZvciBsYXlvdXQgb2YgbmV4dCBzZWN0aW9uDQpgYGANCg0KIyA3LjIgREUgYW5hbHlzaXMgd2l0aCBMTU0NCg0KQSBjb21tb24gc3RhdGlzdGljYWwgYXBwcm9hY2ggaXMgdG8gdXNlIGEgbGluZWFyIG1peGVkLWVmZmVjdCBtb2RlbA0KKExNTSkuIFRoZSBMTU0gYWxsb3dzIHRoZSB1c2VyIHRvIGFjY291bnQgZm9yIHRoZSBzdWJzYW1wbGluZyBwZXINCnRpc3N1ZTsgaW4gb3RoZXIgd29yZHMsIHdlIGFkanVzdCBmb3IgdGhlIGZhY3QgdGhhdCB0aGUgbXVsdGlwbGUgcmVnaW9ucw0Kb2YgaW50ZXJlc3QgcGxhY2VkIHBlciB0aXNzdWUgc2VjdGlvbiBhcmUgbm90IGluZGVwZW5kZW50IG9ic2VydmF0aW9ucywNCmFzIGlzIHRoZSBhc3N1bXB0aW9uIHdpdGggb3RoZXIgdHJhZGl0aW9uYWwgc3RhdGlzdGljYWwgdGVzdHMuIFRoZQ0KZm9ybXVsYXRpb24gb2YgdGhlIExNTSBtb2RlbCBkZXBlbmRzIG9uIHRoZSBzY2llbnRpZmljIHF1ZXN0aW9uIGJlaW5nDQphc2tlZC4NCg0KT3ZlcmFsbCwgdGhlcmUgYXJlIHR3byBmbGF2b3JzIG9mIHRoZSBMTU0gbW9kZWwgd2hlbiB1c2VkIHdpdGggR2VvTXgNCmRhdGE6IGkpIHdpdGggYW5kIGlpKSB3aXRob3V0IHJhbmRvbSBzbG9wZS4NCg0KV2hlbiBjb21wYXJpbmcgZmVhdHVyZXMgdGhhdCBjby1leGlzdCBpbiBhIGdpdmVuIHRpc3N1ZSBzZWN0aW9uLCBhDQpyYW5kb20gc2xvcGUgaXMgaW5jbHVkZWQgaW4gdGhlIExNTSBtb2RlbC4gV2hlbiBjb21wYXJpbmcgZmVhdHVyZXMgdGhhdA0KYXJlIG11dHVhbGx5IGV4Y2x1c2l2ZSBpbiBhIGdpdmVuIHRpc3N1ZSBzZWN0aW9uIHRoZSBMTU0gbW9kZWwgZG9lcyBub3QNCnJlcXVpcmUgYSByYW5kb20gc2xvcGUuDQoNCk1vc3RseSwgd2UgdXNlIHBhdGllbnQvc2FtcGxlIGFzIFJhbmRvbSBJbnRlcmNlcHQgd2hlbiB0aGV5IGFyZSBjb21iaW5lZA0Kb24gc2xpZGVzLg0KDQohW10oaHR0cDovL3VzZXEubmwvd3AtY29udGVudC91cGxvYWRzLzIwMjIvMTIvTE1NX3NldHVwLnBuZykNCg0KYGBge3IgTE1NIHJlZ2lvbn0NCiMgY29udmVydCB0ZXN0IHZhcmlhYmxlcyB0byBmYWN0b3JzDQpwRGF0YSh0YXJnZXRfRGF0YSkkdGVzdENsYXNzIDwtIGZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMSwgdW5pcXVlKHRhcmdldF9EYXRhJEFOTjEpKQ0KcERhdGEodGFyZ2V0X0RhdGEpW1sic2xpZGUiXV0gPC0gZmFjdG9yKHBEYXRhKHRhcmdldF9EYXRhKVtbInNsaWRlX25hbWUiXV0pDQojYXNzYXlEYXRhRWxlbWVudChvYmplY3QgPSB0YXJnZXRfRGF0YSwgZWx0ID0gImxvZ19xIikgPC0gYXNzYXlEYXRhQXBwbHkodGFyZ2V0X0RhdGEsIDIsIEZVTiA9IGxvZywgYmFzZSA9IDIsIGVsdCA9ICJxX25vcm0iKQ0KDQojIHJ1biBMTU06DQojIGZvcm11bGEgZm9sbG93cyBjb252ZW50aW9ucyBkZWZpbmVkIGJ5IHRoZSBsbWU0IHBhY2thZ2UNCmxtbV9yZXN1bHRzIDwtIGMoKQ0KZm9yIChyZWdpb24gaW4gdW5pcXVlKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zKSkgew0KICAgIGluZCA8LSBwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMyA9PSByZWdpb24NCg0KICAgIG1peGVkT3V0bWMgPC0NCiAgICAgICAgbWl4ZWRNb2RlbERFKHRhcmdldF9EYXRhWyxpbmRdLA0KICAgICAgICAgICAgICAgICAgICAgZWx0ID0gImxvZ19xIiwNCiAgICAgICAgICAgICAgICAgICAgIG1vZGVsRm9ybXVsYSA9IH4gdGVzdENsYXNzICsgKDEgfCBzbGlkZSksDQogICAgICAgICAgICAgICAgICAgICBncm91cFZhciA9ICJ0ZXN0Q2xhc3MiLA0KICAgICAgICAgICAgICAgICAgICAgbkNvcmVzID0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCksDQogICAgICAgICAgICAgICAgICAgICBtdWx0aUNvcmUgPSBUUlVFKQ0KDQogICAgIyBmb3JtYXQgcmVzdWx0cyBhcyBkYXRhLmZyYW1lDQogICAgcl90ZXN0IDwtIGRvLmNhbGwocmJpbmQsIG1peGVkT3V0bWNbImxzbWVhbnMiLCBdKQ0KICAgIHRlc3RzIDwtIHJvd25hbWVzKHJfdGVzdCkNCiAgICByX3Rlc3QgPC0gYXMuZGF0YS5mcmFtZShyX3Rlc3QpDQogICAgcl90ZXN0JENvbnRyYXN0IDwtIHRlc3RzDQoNCiAgICAjIHVzZSBsYXBwbHkgaW4gY2FzZSB5b3UgaGF2ZSBtdWx0aXBsZSBsZXZlbHMgb2YgeW91ciB0ZXN0IGZhY3RvciB0bw0KICAgICMgY29ycmVjdGx5IGFzc29jaWF0ZSBnZW5lIG5hbWUgd2l0aCBpdCdzIHJvdyBpbiB0aGUgcmVzdWx0cyB0YWJsZQ0KICAgIHJfdGVzdCRHZW5lIDwtDQogICAgICAgIHVubGlzdChsYXBwbHkoY29sbmFtZXMobWl4ZWRPdXRtYyksDQogICAgICAgICAgICAgICAgICAgICAgcmVwLCBucm93KG1peGVkT3V0bWNbImxzbWVhbnMiLCBdW1sxXV0pKSkNCiAgICByX3Rlc3QkU3Vic2V0IDwtIHJlZ2lvbg0KICAgIHJfdGVzdCRGRFIgPC0gcC5hZGp1c3Qocl90ZXN0JGBQcig+fHR8KWAsIG1ldGhvZCA9ICJmZHIiKQ0KICAgIHJfdGVzdCA8LSByX3Rlc3RbLCBjKCJHZW5lIiwgIlN1YnNldCIsICJDb250cmFzdCIsICJFc3RpbWF0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIlByKD58dHwpIiwgIkZEUiIpXQ0KICAgIA0KICAgIGxtbV9yZXN1bHRzIDwtIHJiaW5kKGxtbV9yZXN1bHRzLCByX3Rlc3QpDQp9DQpgYGANCg0KIyA3LjMgVnVsY2Fub3Bsb3QgKyB0YWJsZSBvZiBMTU0NCg0KYGBge3IgbG1tX3Z1bGNhbm8sIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTEwfQ0KIyBDYXRlZ29yaXplIFJlc3VsdHMgYmFzZWQgb24gUC12YWx1ZSAmIEZEUiBmb3IgcGxvdHRpbmcNCmZjX3RocmVzaG9sZCA9IDENCg0KbG1tX3Jlc3VsdHMkQ29sb3IgPC0gcGFzdGUoIk5TIG9yIEZDIDwgIixmY190aHJlc2hvbGQgPSAxKQ0KbG1tX3Jlc3VsdHMkQ29sb3JbbG1tX3Jlc3VsdHMkYFByKD58dHwpYCA8IDAuMDVdIDwtICJQIDwgMC4wNSINCmxtbV9yZXN1bHRzJENvbG9yW2xtbV9yZXN1bHRzJEZEUiA8IDAuMDVdIDwtICJGRFIgPCAwLjA1Ig0KbG1tX3Jlc3VsdHMkQ29sb3JbbG1tX3Jlc3VsdHMkRkRSIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCmxtbV9yZXN1bHRzJENvbG9yW2FicyhsbW1fcmVzdWx0cyRFc3RpbWF0ZSkgPCBmY190aHJlc2hvbGRdIDwtIHBhc3RlKCJOUyBvciBGQyA8ICIsZmNfdGhyZXNob2xkID0gMSkNCmxtbV9yZXN1bHRzJENvbG9yIDwtIGZhY3RvcihsbW1fcmVzdWx0cyRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMSIsICJQIDwgMC4wNSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQpjb3VudGVyID0gMQ0KcGxvdHNfbG1tIDwtIGxpc3QoKQ0KZm9yKGMgaW4gdW5pcXVlKGxtbV9yZXN1bHRzJFN1YnNldCkgKSB7DQogIGxtbV9yZXN1bHRzX3NsaWNlID0gbG1tX3Jlc3VsdHNbbG1tX3Jlc3VsdHMkU3Vic2V0ID09IGMsXQ0KDQogICMgcGljayB0b3AgZ2VuZXMgZm9yIGVpdGhlciBzaWRlIG9mIHZvbGNhbm8gdG8gbGFiZWwNCiAgIyBvcmRlciBnZW5lcyBmb3IgY29udmVuaWVuY2U6DQogIGxtbV9yZXN1bHRzX3NsaWNlJGludmVydF9QIDwtICgtbG9nMTAobG1tX3Jlc3VsdHNfc2xpY2UkYFByKD58dHwpYCkpICogc2lnbihsbW1fcmVzdWx0c19zbGljZSRFc3RpbWF0ZSkNCiAgDQogICNsb29wIGhlcmUgb3ZlciB0ZXN0ZWQgY29uZGl0aW9ucyBpZiBhcHBsaWNhYmxlDQogIHRvcF9nIDwtIGMoKQ0KICB0b3BfZyA8LSBjKHRvcF9nLA0KICAgIGxtbV9yZXN1bHRzX3NsaWNlWywgJ0dlbmUnXVsNCiAgICAgIG9yZGVyKGxtbV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjE1XV0sDQogICAgbG1tX3Jlc3VsdHNfc2xpY2VbLCAnR2VuZSddWw0KICAgICAgb3JkZXIobG1tX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjE1XV0pDQoNCiAgdG9wX2cgPC0gdW5pcXVlKHRvcF9nKQ0KICANCiAgbG1tX3Jlc3VsdHNfc2xpY2UgPC0gbG1tX3Jlc3VsdHNfc2xpY2VbLCAtMSpuY29sKGxtbV9yZXN1bHRzX3NsaWNlKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCiAgDQogICMgRmxpcCBDb250cmFzdA0KICBjb250cmFzdF9sYWIgPC0gYXMuY2hhcmFjdGVyKGxtbV9yZXN1bHRzX3NsaWNlJENvbnRyYXN0KQ0KICBjb250cmFzdF9sYWIgPC0gc3Ryc3BsaXQoY29udHJhc3RfbGFiLCAiLSIpW1sxXV0NCiAgY29udHJhc3RfbGFiIDwtIHBhc3RlKGNvbnRyYXN0X2xhYlsyXSwgIi0iLCBjb250cmFzdF9sYWJbMV0pDQogIA0KICAjIEdyYXBoIHJlc3VsdHMNCiAgcGxvdHNfbG1tW1tjb3VudGVyXV0gPC0gZ2dwbG90KGxtbV9yZXN1bHRzX3NsaWNlLA0KICAgICAgICAgICAgICAgYWVzKHggPSBFc3RpbWF0ZSwgeSA9IC1sb2cxMChgUHIoPnx0fClgKSwNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IEdlbmUpKSArDQogICAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYyhmY190aHJlc2hvbGQsIC1mY190aHJlc2hvbGQpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgICAgICBnZW9tX3BvaW50KCkgKw0KICAgICAgICAgIGxhYnMoDQogICAgICAgICAgICB4ID0gcGFzdGUoY29udHJhc3RfbGFiLCAiIGxvZzIoRkMpIiksDQogICAgICAgICAgICAgICB5ID0gIlNpZ25pZmljYW5jZSwgLWxvZzEwKFApIiwNCiAgICAgICAgICAgICAgIGNvbG9yID0gIlNpZ25pZmljYW5jZSIpICsNCiAgICAgICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBGRFIgPCAwLjA1YCA9ICJsaWdodGJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBQIDwgMC4wNWAgPSAib3JhbmdlMiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMWAgPSAiZ3JheSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsMC4wNSkpKSArDQogICAgICAgICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQobG1tX3Jlc3VsdHNfc2xpY2UsICBgUHIoPnx0fClgIDwgMC4wMDEgJiBhYnMobG1tX3Jlc3VsdHNfc2xpY2UkRXN0aW1hdGUpID4gZmNfdGhyZXNob2xkICYgR2VuZSAlaW4lIHRvcF9nKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMTUsIGNvbG9yID0gImJsYWNrIixzaXplPTUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5zZWdtZW50Lmxlbmd0aCA9IC4xLCBib3gucGFkZGluZyA9IC4yLCBsd2QgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgICAgICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArDQogICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICAgICAgICBmYWNldF93cmFwKH5TdWJzZXQsIHNjYWxlcyA9ICJmcmVlX3kiKQ0KICBjb3VudGVyIDwtIGNvdW50ZXIgKyAxDQp9DQpncmlkLmFycmFuZ2UoZ3JvYnM9cGxvdHNfbG1tLG5jb2w9MikNCmBgYCAgICANCg0KYGBge3IgdGFibGVfb2ZfTE1NX3Jlc3VsdHMgcmVnaW9ufQ0KI3N1YnNldChsbW1fcmVzdWx0cywgR2VuZSAlaW4lIEdPSSkNCmRhdGF0YWJsZShsbW1fcmVzdWx0cywgcm93bmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCBvcHRpb25zID0gbGlzdCAoDQogICAgICAgICAgICAgIGRvbSA9ICdCZnRyaXAnLA0KICAgICAgICAgICAgICBidXR0b25zID0gYygnY29weScsICdjc3YnLCAnZXhjZWwnLCAncGRmJywgJ3ByaW50JykNCiAgICAgICAgICAgICksDQogICAgICAgICAgY2FwdGlvbiA9ICJERSByZXN1bHRzIGZvciBHZW5lcyBvZiBJbnRlcmVzdCAoPjc1JSBDVikiLGZpbHRlcj0ndG9wJykgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygiRXN0aW1hdGUiLCJQcig+fHR8KSIsIkZEUiIpLCBkaWdpdHM9MykNCmBgYA0KDQojIDcuNCBQbG90dGluZyBHZW5lcyBvZiBJbnRlcmVzdA0KDQpgYGB7ciBwbG90X2dlbmVfb2ZfaW50ZXJlc3QsIGZpZy53aWR0aD0xMCxmaWcuaGVpZ2h0PTV9DQpteV9nb2lzIDwtdW5pcXVlKHN1YnNldChsbW1fcmVzdWx0cywgYEZEUmAgPCAwLjAwMSkkR2VuZSkNCnRtcF90Ymw8LXN1YnNldChsbW1fcmVzdWx0cywgR2VuZSAlaW4lIG15X2dvaXMpDQoNCm15X2dvaXMgPC0gYygpDQpmb3IgKGNvbnRyYXN0IGluIHVuaXF1ZSh0bXBfdGJsJENvbnRyYXN0KSkgeyAjIGdlbmUgb2YgaW50ZXJlc3QgZm9yIGFsbCBjb250cmFzdHMNCiNmb3IgKGNvbnRyYXN0IGluIGMoIkp1eF9HbG8gLSBQcm9fVHViIikpIHsNCiAgaW5kIDwtIHRtcF90YmwkQ29udHJhc3QgPT0gY29udHJhc3QNCiAgZ29pIDwtIHRtcF90YmxbaW5kLCAiR2VuZSJdW29yZGVyKHRtcF90YmxbaW5kLCAiRXN0aW1hdGUiXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjNdXQ0KICBteV9nb2lzIDwtIGMobXlfZ29pcywgZ29pKQ0KICBnb2kgPC0gdG1wX3RibFtpbmQsICJHZW5lIl1bb3JkZXIodG1wX3RibFtpbmQsICJFc3RpbWF0ZSJdLCBkZWNyZWFzaW5nID0gVFJVRSlbMTozXV0NCiAgbXlfZ29pcyA8LSBjKG15X2dvaXMsIGdvaSkNCn0NCg0KaWYobnJvdyh0bXBfdGJsKSA+IDEpIHsgDQogIGRhdGF0YWJsZSh0bXBfdGJsLHJvd25hbWVzID0gRkFMU0UsY2FwdGlvbiA9ICJERSByZXN1bHRzIGZvciBHZW5lcyBvZiBJbnRlcmVzdCAiLGZpbHRlcj0ndG9wJykgJT4lIGZvcm1hdFJvdW5kKGNvbHVtbnM9YygiRXN0aW1hdGUiLCJQcig+fHR8KSIsIkZEUiIpLCBkaWdpdHM9MykNCiANCmZvciAobXlfZ29pIGluIG15X2dvaXMpIHsNCiMgc2hvdyBleHByZXNzaW9uIGZvciBhIHNpbmdsZSB0YXJnZXQNCiAgYTwtZ2dwbG90KHBEYXRhKHRhcmdldF9EYXRhKSwNCiAgICAgICBhZXMoeCA9IEFOTjIsIGZpbGwgPSBBTk4yLA0KICAgICAgICAgICB5ID0gYXMubnVtZXJpYyhhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhW215X2dvaSwgXSwgZWx0ID0gInFfbm9ybSIpKSkpICsNCiAgZ2VvbV92aW9saW4oKSArDQogIGdlb21faml0dGVyKHdpZHRoID0gLjIpICsNCiAgbGFicyh5ID0gcGFzdGUobXlfZ29pLCJFeHByZXNzaW9uIikpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArDQogICNmYWNldF93cmFwKH5BTk4zLCBucm93PTEpICsgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpICsNCiAgdGhlbWVfYncoKSArIGNvb3JkX2ZsaXAoKQ0KICBwcmludChhKQ0KfQ0KfWVsc2V7DQogIHByaW50KCJObyBzaWduaWZpY2FudCBsTU0gcmVzdWx0cyB0byBwbG90IikNCn0NCmBgYA0KDQojIDcuNSBIZWF0bWFwIG9mIFNpZ25pZmljYW50IEdlbmVzDQoNCkluIGFkZGl0aW9uIHRvIGdlbmVyYXRpbmcgaW5kaXZpZHVhbCBnZW5lIGJveCBwbG90cyBvciB2b2xjYW5vIHBsb3RzLCB3ZQ0KY2FuIGFnYWluIGNyZWF0ZSBhIGhlYXRtYXAgZnJvbSBvdXIgZGF0YS4gVGhpcyB0aW1lIHJhdGhlciB0aGFuDQp1dGlsaXppbmcgQ1YgdG8gc2VsZWN0IGdlbmVzLCB3ZSBjYW4gdXNlIHRoZSBQLXZhbHVlIG9yIEZEUiB2YWx1ZXMgdG8NCnNlbGVjdCBnZW5lcy4gSGVyZSwgd2UgcGxvdCBhbGwgZ2VuZXMgd2l0aCBhbiBGRFIgXDwgMC4wMDEuDQoNCmBgYHtyIGhlYXRtYXBfc2lnX2dlbmVzLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0yMH0NCm15X2dvaXMgPC11bmlxdWUoc3Vic2V0KGxtbV9yZXN1bHRzLCBgRkRSYCA8IDAuMDAxKSRHZW5lKSAgICMgMTAwIHRvIHByZXZlbnQgbG9uZyBydW50aW1lDQoNCmlmKGxlbmd0aChteV9nb2lzKTwyKSB7DQogIHByaW50KCJObyBzaWduaWZpY2FudCByZXN1bHRzIHRvIHNob3ciKQ0KIA0KfWVsc2V7DQoNCnBoZWF0bWFwKGxvZzIoYXNzYXlEYXRhRWxlbWVudCh0YXJnZXRfRGF0YVtteV9nb2lzLCBdLCBlbHQgPSAicV9ub3JtIikpLA0KICAgICAgICAgc2NhbGUgPSAicm93IiwNCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMywgY3V0cmVlX3Jvd3MgPSAyLA0KICAgICAgICAgYnJlYWtzID0gc2VxKC0zLCAzLCAwLjA1KSwNCiAgICAgICAgIGNvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJwdXJwbGUzIiwgImJsYWNrIiwgInllbGxvdzIiKSkoMTIwKSwNCiAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gY29sb3JfbGlzdCwNCiAgICAgICAgIGFubm90YXRpb25fY29sID0gcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX25hbWVzXSkNCn0NCmBgYA0KDQojIDggUGF0aHdheSBBbmFseXNpcw0KDQpQYXRod2F5IGFuYWx5c2lzIGVuYWJsZXMgZXhwbG9yYXRpb24gb2YgZGlmZmVyZW50IGFnZ3JlZ2F0ZSBnZW5lIHNldHMNCmZvciBvdXIgZXhwZXJpbWVudGFsIHF1ZXN0aW9ucy4gRWFjaCBpbmRpdmlkdWFsIFJPSS9BT0kgc2VnbWVudCBpcw0Kc2NvcmVkIGZvciBldmVyeSBwYXRod2F5IG9mIGludGVyZXN0LCB3aGljaCB3ZSBjYW4gdGhlbiB1c2UgdG8NCmludmVzdGlnYXRlIGJpb2xvZ2ljYWwgZGlmZmVyZW5jZXMuIFdlIHdpbGwgcGVyZm9ybSBhbmFsb2dvdXMgYW5hbHlzZXMNCmFzIHRob3NlIG91dGxpbmVkIGluIHRoZSBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBhbmQgU3BhdGlhbA0KRGVjb252b2x1dGlvbiBzZWN0aW9ucyBvZiB0aGUgcmVwb3J0IGZvciBnZW5lIHNldCBlbnJpY2htZW50Lg0KDQojIDguMSBTY29yaW5nIEdlbmUgU2V0cw0KDQpQYXRod2F5cyBhbmQgZ2VuZSBzZXRzIHdlcmUgZGVmaW5lZCBmcm9tIHRoZSBLZWdnIEJyaXRlIGRhdGFiYXNlLiBXZSB1c2UNCmFuIFIgc29mdHdhcmUgcGFja2FnZSBjYWxsZWQgR2VuZSBTZXQgVmFyaWF0aW9uIEFuYWx5c2lzIHRvIHNjb3JlIGVhY2gNCnNlZ21lbnQgd2l0aGluIG91ciBzdHVkeS4gc2VlDQo8aHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL21zaWdkYnIvdmlnbmV0dGVzL21zaWdkYnItaW50cm8uaHRtbD4NCmZvciBvcHRpb25zIG9uIGNvbGxlY3Rpb25zLiBXZSB1c2UgdGhlIEtFR0cgYW5kIFJFQUNUT01FLg0KDQpgYGB7ciBidWlsZF9nZW5lc2V0c30NCiNnYygpDQpoX2dlbmVfc2V0cyA9IHJiaW5kKG1zaWdkYnIoc3BlY2llcyA9ICJodW1hbiIsIHN1YmNhdGVnb3J5ID0gIkNQOktFR0ciKSwNCiAgICAgICAgICAgICAgICAgICAgbXNpZ2RicihzcGVjaWVzID0gImh1bWFuIiwgc3ViY2F0ZWdvcnkgPSAiQ1A6UkVBQ1RPTUUiKSkNCiNtc2lnZGJyKHNwZWNpZXMgPSAiaHVtYW4iLCBzdWJjYXRlZ29yeSA9ICJDUDpCSU9DQVJUQSIpDQoNCm1zaWdkYnJfbGlzdCA9IHNwbGl0KHggPSBoX2dlbmVfc2V0cyRnZW5lX3N5bWJvbCwgZiA9IGhfZ2VuZV9zZXRzJGdzX25hbWUpDQoNCiMgcHJlcGFyZSBkZiBmb3IgYWNjdXJhdGUgbWVyZ2luZyBiYWNrIGdlbmVzIGxhdGVyDQpwd19nZW5lX2RmPC1kYXRhLmZyYW1lKFBhdGh3YXkgPSBuYW1lcyhtc2lnZGJyX2xpc3QpKQ0KcHdfZ2VuZV9kZiRnZW5lczwtbXNpZ2Ricl9saXN0DQpgYGANCg0KYGBge3IgcnVuX2dzdmF9DQpzc2dzZWFfcmVzdWx0cyA8LSBHU1ZBOjpnc3ZhKGV4cHIgPSBhc3NheURhdGFFbGVtZW50KHRhcmdldF9EYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVsdCA9ICJsb2dfcSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdzZXQuaWR4Lmxpc3QgPSBtc2lnZGJyX2xpc3QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWV0aG9kID0gInNzZ3NlYSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLnN6ID0gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXguc3ogPSA1MDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQ0KZ2VuZVNldE9iaiA8LQ0KICBOYW5vU3RyaW5nR2VvTXhTZXQoYXNzYXlEYXRhID0gc3Nnc2VhX3Jlc3VsdHMsDQogICAgICAgICAgICAgICAgICAgICBwaGVub0RhdGEgPSBBbm5vdGF0ZWREYXRhRnJhbWUocERhdGEodGFyZ2V0X0RhdGEpKSwNCiAgICAgICAgICAgICAgICAgICAgIHByb3RvY29sRGF0YSA9IHByb3RvY29sRGF0YSh0YXJnZXRfRGF0YSksDQogICAgICAgICAgICAgICAgICAgICBmZWF0dXJlVHlwZSA9ICJHZW5lU2V0IiwNCiAgICAgICAgICAgICAgICAgICAgIGNoZWNrID0gRkFMU0UpDQpgYGANCg0KIyA4LjIgRGlmZmVyZW50YWwgYW5hbHlzaXMgb2YgcGF0aHdheXMNCg0KYGBge3IgTE1NX29mX3BhdGh3YXlfYW5hbGlzaXMgcmVnaW9ufQ0KIyAjIGNvbnZlcnQgdGVzdCB2YXJpYWJsZXMgdG8gZmFjdG9ycw0KcERhdGEodGFyZ2V0X0RhdGEpJHRlc3RDbGFzcyA8LSBmYWN0b3IocERhdGEodGFyZ2V0X0RhdGEpJEFOTjEsIHVuaXF1ZSh0YXJnZXRfRGF0YSRBTk4xKSkNCnBEYXRhKGdlbmVTZXRPYmopW1sic2xpZGUiXV08LWZhY3RvcihwRGF0YShnZW5lU2V0T2JqKVtbInNsaWRlX25hbWUiXV0pDQojcERhdGEodGFyZ2V0X0RhdGEpJHRlc3RSZWdpb248LWZhY3RvcihwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMiwgdW5pcXVlKGNvdW50X21hdCRBTk4zKSkNCg0KbG1tX3NzZ3NlYV9yZXN1bHRzIDwtIGMoKQ0KZm9yIChyZWdpb24gaW4gdW5pcXVlKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zKSkgew0KICBpbmQgPC0gcERhdGEodGFyZ2V0X0RhdGEpJEFOTjMgPT0gcmVnaW9uDQogIG1peGVkT3V0bWMgPC0NCiAgICBtaXhlZE1vZGVsREUoZ2VuZVNldE9ialssIGluZF0sDQogICAgICAgICAgICAgICAgIGVsdCA9ICJleHBycyIsDQogICAgICAgICAgICAgICAgIG1vZGVsRm9ybXVsYSA9IH4gdGVzdENsYXNzICsgKDEgfCBzbGlkZSksDQogICAgICAgICAgICAgICAgIGdyb3VwVmFyID0gInRlc3RDbGFzcyIsDQogICAgICAgICAgICAgICAgIG5Db3JlcyA9IHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpLA0KICAgICAgICAgICAgICAgICBtdWx0aUNvcmUgPSBUUlVFKQ0KDQogICMgZm9ybWF0IHJlc3VsdHMgYXMgZGF0YS5mcmFtZQ0KICByX3NzZ3NlYV90ZXN0IDwtIGRvLmNhbGwocmJpbmQsIG1peGVkT3V0bWNbImxzbWVhbnMiLCBdKQ0KICBzc2dzZWFfdGVzdHMgPC0gcm93bmFtZXMocl9zc2dzZWFfdGVzdCkNCiAgcl9zc2dzZWFfdGVzdCA8LSBhcy5kYXRhLmZyYW1lKHJfc3Nnc2VhX3Rlc3QpDQogIHJfc3Nnc2VhX3Rlc3QkQ29udHJhc3QgPC0gc3Nnc2VhX3Rlc3RzDQogICNyX3NzZ3NlYV90ZXN0JEdlbmVzIDwtIG1zaWdkYnJfbGlzdCBzZWVtcyB1bnJlbGlhYmxlIGFzIGdzdmEgb21pdHMgcGF0aHdheXMgaWYgZ2VuZXMgYXJlIG5vdCBpbiBkYXRhLi4NCg0KICAjIHVzZSBsYXBwbHkgaW4gY2FzZSB5b3UgaGF2ZSBtdWx0aXBsZSBsZXZlbHMgb2YgeW91ciB0ZXN0IGZhY3RvciB0bw0KICAjIGNvcnJlY3RseSBhc3NvY2lhdGUgZ2VuZSBuYW1lIHdpdGggaXQncyByb3cgaW4gdGhlIHJlc3VsdHMgdGFibGUNCiAgcl9zc2dzZWFfdGVzdCRQYXRod2F5IDwtDQogICAgdW5saXN0KGxhcHBseShjb2xuYW1lcyhtaXhlZE91dG1jKSwNCiAgICAgICAgICAgICAgICAgIHJlcCwgbnJvdyhtaXhlZE91dG1jWyJsc21lYW5zIiwgXVtbMV1dKSkpDQoNCiAgI3Jfc3Nnc2VhX3Rlc3QkU3Vic2V0IDwtIHN0YXR1cw0KICByX3NzZ3NlYV90ZXN0JEZEUiA8LSBwLmFkanVzdChyX3NzZ3NlYV90ZXN0JGBQcig+fHR8KWAsIG1ldGhvZCA9ICJmZHIiKQ0KICByX3NzZ3NlYV90ZXN0JFN1YnNldCA8LSByZWdpb24NCiAgcl9zc2dzZWFfdGVzdCA8LSByX3NzZ3NlYV90ZXN0WywgYygiUGF0aHdheSIsICJTdWJzZXQiLCAiQ29udHJhc3QiLCAiRXN0aW1hdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQcig+fHR8KSIsICJGRFIiKV0NCiAgbG1tX3NzZ3NlYV9yZXN1bHRzIDwtIHJiaW5kKGxtbV9zc2dzZWFfcmVzdWx0cywgcl9zc2dzZWFfdGVzdCkNCg0KICB9DQogIGxtbV9zc2dzZWFfcmVzdWx0cyA8LSBtZXJnZShsbW1fc3Nnc2VhX3Jlc3VsdHMsIHB3X2dlbmVfZGYpDQpgYGANCg0KIyA4LjIuMSBUYWJsZSBvZiBEaWZmZXJlbnRhbCBhbmFseXNpcyBvZiBwYXRod2F5cw0KDQpgYGB7ciB0YWJsZV9vZl9MTU1fcGF0aHdheV9yZXN1bHRzIHJlZ2lvbiwgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MjB9DQpkYXRhdGFibGUoc3Vic2V0KGxtbV9zc2dzZWFfcmVzdWx0cyksIHJvd25hbWVzID0gRkFMU0UsDQogICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QgKA0KICAgICAgICAgICAgIHBhZ2VMZW5ndGggPSAxMCwNCiAgICAgICAgICAgICAgZG9tID0gJ0JmdHJpcCcsDQogICAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKQ0KICAgICAgICAgICAgKSwNCiAgICAgICAgICBjYXB0aW9uID0gIkRFIHJlc3VsdHMgZm9yIFBhdGh3YXlzIixmaWx0ZXI9J3RvcCcpICU+JSBmb3JtYXRSb3VuZChjb2x1bW5zPWMoIkVzdGltYXRlIiwiUHIoPnx0fCkiLCJGRFIiKSwgZGlnaXRzPTUpDQpgYGANCg0KIyA4LjMgVnVsY2Fub3Bsb3Qgb2YgTE1NX1BhdGh3YXlzDQoNCmBgYHtyIGxtbV9wd192dWxjYW5vLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMH0NCiMgQ2F0ZWdvcml6ZSBSZXN1bHRzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQpmY190aHJlc2hvbGQgPSAwLjMNCg0KbG1tX3NzZ3NlYV9yZXN1bHRzJENvbG9yIDwtICJOUyBvciBGQyA8IDAuMyINCmxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvcltsbW1fc3Nnc2VhX3Jlc3VsdHMkYFByKD58dHwpYCA8IDAuMDVdIDwtICJQIDwgMC4wNSINCmxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvcltsbW1fc3Nnc2VhX3Jlc3VsdHMkRkRSIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3JbbG1tX3NzZ3NlYV9yZXN1bHRzJEZEUiA8IDAuMDAxXSA8LSAiRkRSIDwgMC4wMDEiDQpsbW1fc3Nnc2VhX3Jlc3VsdHMkQ29sb3JbYWJzKGxtbV9zc2dzZWFfcmVzdWx0cyRFc3RpbWF0ZSkgPCBmY190aHJlc2hvbGRdIDwtICJOUyBvciBGQyA8IDAuMyINCmxtbV9zc2dzZWFfcmVzdWx0cyRDb2xvciA8LSBmYWN0b3IobG1tX3NzZ3NlYV9yZXN1bHRzJENvbG9yLA0KICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTlMgb3IgRkMgPCAwLjMiLCAiUCA8IDAuMDUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkRSIDwgMC4wNSIsICJGRFIgPCAwLjAwMSIpKQ0KDQojIHBpY2sgdG9wIHB3IGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQojIG9yZGVyIHB3IGZvciBjb252ZW5pZW5jZToNCmxtbV9zc2dzZWFfcmVzdWx0cyRpbnZlcnRfUCA8LSAoLWxvZzEwKGxtbV9zc2dzZWFfcmVzdWx0cyRgUHIoPnx0fClgKSkgKiBzaWduKGxtbV9zc2dzZWFfcmVzdWx0cyRFc3RpbWF0ZSkNCnRvcF9zc2dzZWFfZyA8LSBjKCkNCg0KI2xvb3AgaGVyZSBvdmVyIHRlc3RlZCBjb25kaXRpb25zIGlmIGFwcGxpY2FibGUNCg0KZm9yKGMgaW4gdW5pcXVlKGxtbV9zc2dzZWFfcmVzdWx0cyRTdWJzZXQpICkgew0KICBsbW1fcmVzdWx0c19zbGljZSA9IGxtbV9zc2dzZWFfcmVzdWx0c1tsbW1fc3Nnc2VhX3Jlc3VsdHMkU3Vic2V0ID09IGMsXQ0KIHRvcF9zc2dzZWFfZyA8LSBjKHRvcF9zc2dzZWFfZywNCiAgICAgICAgIGxtbV9zc2dzZWFfcmVzdWx0c1tpbmQsICdQYXRod2F5J11bDQogICAgICAgICAgICAgICBvcmRlcihsbW1fc3Nnc2VhX3Jlc3VsdHNbaW5kLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTVdXSwNCiAgICAgICAgIGxtbV9zc2dzZWFfcmVzdWx0c1tpbmQsICdQYXRod2F5J11bDQogICAgICAgICAgICAgICBvcmRlcihsbW1fc3Nnc2VhX3Jlc3VsdHNbaW5kLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjE1XV0pDQp9DQogDQp0b3Bfc3Nnc2VhX2cgPC0gdW5pcXVlKHRvcF9zc2dzZWFfZykNCmxtbV9zc2dzZWFfcmVzdWx0cyA8LSBsbW1fc3Nnc2VhX3Jlc3VsdHNbLCAtMSpuY29sKGxtbV9zc2dzZWFfcmVzdWx0cyldICMgcmVtb3ZlIGludmVydF9QIGZyb20gbWF0cml4DQoNCiNsbW1fc3Nnc2VhX3Jlc3VsdHNfc2xpY2UgPC0gbG1tX3NzZ3NlYV9yZXN1bHRzX3NsaWNlW2xtbV9zc2dzZWFfcmVzdWx0c19zbGljZSRGRFIgPCAxLF0NCg0KIyBHcmFwaCByZXN1bHRzDQpnZ3Bsb3QobG1tX3NzZ3NlYV9yZXN1bHRzLA0KICAgICAgIGFlcyh4ID0gRXN0aW1hdGUsIHkgPSAtbG9nMTAoYFByKD58dHwpYCksDQogICAgICAgICAgIGNvbG9yID0gQ29sb3IsIGxhYmVsID0gUGF0aHdheSkpICsNCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKDAuMiwgLTAuMiksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKDAuMDUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgIGdlb21fcG9pbnQoKSArDQogICAgbGFicyh4ID0gcGFzdGUobG1tX3Jlc3VsdHNfc2xpY2UkQ29udHJhc3QsICJsb2cyKEZDKSIpLA0KICAgICAgICAgeSA9ICJTaWduaWZpY2FuY2UsIC1sb2cxMChQKSIsDQogICAgICAgICBjb2xvciA9ICJTaWduaWZpY2FuY2UiKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoYEZEUiA8IDAuMDAxYCA9ICJkb2RnZXJibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgRkRSIDwgMC4wNWAgPSAibGlnaHRibHVlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgUCA8IDAuMDVgID0gIm9yYW5nZTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBOUyBvciBGQyA8IDAuNWAgPSAiZ3JheSIpLA0KICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemUgPSA0KSkpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsMC4wNSkpKSArDQogICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBzdWJzZXQobG1tX3NzZ3NlYV9yZXN1bHRzLCBDb2xvciA9PSAiRkRSIDwgMC4wNSIgfCBDb2xvciA9PSAiRkRSIDwgMC4wMDEiKSwNCiAgICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gMC4xNSwgY29sb3IgPSAiYmxhY2siLHNpemU9MywNCiAgICAgICAgICAgICAgICAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAuMSwgYm94LnBhZGRpbmcgPSAuMiwgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICBtYXgub3ZlcmxhcHMgPSA1MCkgKw0KICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsNCiAgICBmYWNldF93cmFwKH5TdWJzZXQsIHNjYWxlcyA9ICJmcmVlX3kiKQ0KDQojICAgICtmYWNldF93cmFwKH5TdWJzZXQsIHNjYWxlcyA9ICJmcmVlX3kiKSkNCmBgYA0KDQojIDguNCBoZWF0bWFwIG9mIHBhdGh3YXlzDQoNCmBgYHtyIHB3X2hlYXRtYXAsIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTIwfQ0KICBhY3RpdmVfcHc8LWZpbHRlcihsbW1fc3Nnc2VhX3Jlc3VsdHMsIGBQcig+fHR8KWAgPCAwLjAwMSkkUGF0aHdheQ0KICBhY3RpdmVfcHc8LWZpbHRlcihsbW1fc3Nnc2VhX3Jlc3VsdHMsIEZEUiA8IDAuMDAxICkkUGF0aHdheQ0KICAjYWN0aXZlX3B3PC1maWx0ZXIobG1tX3NzZ3NlYV9yZXN1bHRzLCBDb2xvciA9PSAiRkRSIDwgMC4wMDEiKSRQYXRod2F5DQogIA0KICANCiAgYWN0aXZlX3B3PC10b3Bfc3Nnc2VhX2cNCiAgDQogIGlmIChsZW5ndGgoYWN0aXZlX3B3KT4xKSB7DQogIA0KICAgIHByaW50KCJnbyIpDQogICAgDQogIHB3X21hdHJpeDwtYXNzYXlEYXRhRWxlbWVudChnZW5lU2V0T2JqLCBlbHQgPSAiZXhwcnMiKQ0KDQogIHBoZWF0bWFwKHB3X21hdHJpeFthY3RpdmVfcHcsXSwNCiAgICAgICAgIHNjYWxlID0gInJvdyIsDQogICAgICAgICBzaG93X3Jvd25hbWVzID0gVFJVRSwgc2hvd19jb2xuYW1lcyA9IFRSVUUsDQogICAgICAgICBmb250c2l6ZV9yb3cgPSAxMCwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgY2x1c3RlcmluZ19tZXRob2QgPSAiYXZlcmFnZSIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJldWNsaWRlYW4iLA0KICAgICAgICAgY3V0cmVlX2NvbHMgPSAyLCBjdXRyZWVfcm93cyA9IDIsDQogICAgICAgICBicmVha3MgPSBzZXEoLTMsIDMsIDAuMDUpLA0KICAgICAgICAgI2NvbG9yID0gY29sb3JSYW1wUGFsZXR0ZShjKCJwdXJwbGUzIiwgImJsYWNrIiwgInllbGxvdzIiKSkoMTIwKSwNCiAgICAgICAgIG1haW4gPSAiSGVhdG1hcCBvZiBzZWxlY3RlZCBQYXRod2F5cyIsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbG9ycyA9IGNvbG9yX2xpc3QsDQogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHBEYXRhKHRhcmdldF9EYXRhKVssIGFubl9uYW1lc10pDQogIA0KICB9ZWxzZXsNCiAgICBwcmludCgiTm8gc2lnbmlmaWNhbnQgcmVzdWx0cyB0byBkaXNwbGF5IikNCiAgfQ0KYGBgDQoNCiMgOC41IGZnc2VhIHBhdGh3YXkgYW5hbHlzaXMNCg0KQW5vdGhlciBvcHRpb24gZm9yIHBhdGh3YXkgYW5hbHlzaXMgaXMgdGhlIFIgc29mdHdhcmUgcGFja2FnZSBjYWxsZWQgRmFzdCBHZW5lIFNldCBFbnJpY2htZW50IEFuYWx5c2lzLiBJdCBpcyBhIHByZS1yYW5rZWQgbWV0aG9kIHRoYXQgdXNlcyB0aGUgTE1NIHJlc3VsdHMgY29tYmluZWQgd2l0aCB0aGUgc2FtZSBwYXRod2F5IGxpc3QgZnJvbSBtc2lnZGJyIGFzIEdTVkEuDQoNCmBgYHtyIGZnc2VhfQ0KZmdzZWFfcmVzdWx0c19hbGwgPC0gYygpDQpmb3IgKGNvbnRyYXN0IGluIHVuaXF1ZShsbW1fcmVzdWx0cyRDb250cmFzdCkpIHsNCiAgICANCiAgICByYW5rcyA8LSBsbW1fcmVzdWx0cyRFc3RpbWF0ZVtsbW1fcmVzdWx0cyRDb250cmFzdCA9PSBjb250cmFzdF0NCiAgICBuYW1lcyhyYW5rcykgPC0gbG1tX3Jlc3VsdHMkR2VuZVtsbW1fcmVzdWx0cyRDb250cmFzdCA9PSBjb250cmFzdF0NCiAgICANCiAgICBmZ3NlYV9yZXN1bHRzIDwtIGZnc2VhKG1zaWdkYnJfbGlzdCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgcmFua3MsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGVwcyA9IDAuMCwNCiAgICAgICAgICAgICAgICAgICAgICAgICBtaW5TaXplPTE1LCANCiAgICAgICAgICAgICAgICAgICAgICAgICBtYXhTaXplID0gNTAwKQ0KICAgIGZnc2VhX3Jlc3VsdHMkQ29udHJhc3QgPC0gY29udHJhc3QNCiAgICBmZ3NlYV9yZXN1bHRzX2FsbCA8LSByYmluZChmZ3NlYV9yZXN1bHRzX2FsbCwgZmdzZWFfcmVzdWx0cykNCiAgICANCn0NCg0KZmdzZWFfcmVhY3RvbWUgPC0gZmdzZWFfcmVzdWx0c19hbGxbZ3JlcCgiUkVBQ1RPTUUiLCBwYXRod2F5KSxdDQpmZ3NlYV9yZWFjdG9tZSRwYXRod2F5IDwtIGdzdWIoIlJFQUNUT01FXyIsICIiLCBmZ3NlYV9yZWFjdG9tZSRwYXRod2F5KQ0KZmdzZWFfcmVhY3RvbWUkcGF0aHdheSA8LSBnc3ViKCJfIiwgIiAiLCBmZ3NlYV9yZWFjdG9tZSRwYXRod2F5KQ0KYGBgDQoNCmBgYHtyIGZnc2VhX3B3X3Z1bGNhbm8sIGZpZy53aWR0aD0yMCxmaWcuaGVpZ2h0PTEwfQ0KIyBDYXRlZ29yaXplIFJlc3VsdHMgYmFzZWQgb24gUC12YWx1ZSAmIEZEUiBmb3IgcGxvdHRpbmcNCmZjX3RocmVzaG9sZCA9IDAuMw0KDQoNCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yIDwtICJOUyBvciBGQyA8IDAuMyINCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yW2Znc2VhX3Jlc3VsdHNfYWxsJHB2YWwgPCAwLjA1XSA8LSAiUCA8IDAuMDUiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvcltmZ3NlYV9yZXN1bHRzX2FsbCRwYWRqIDwgMC4wNV0gPC0gIkZEUiA8IDAuMDUiDQpmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvcltmZ3NlYV9yZXN1bHRzX2FsbCRwYWRqIDwgMC4wMDFdIDwtICJGRFIgPCAwLjAwMSINCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yW2FicyhmZ3NlYV9yZXN1bHRzX2FsbCRFUykgPCBmY190aHJlc2hvbGRdIDwtICJOUyBvciBGQyA8IDAuMyINCmZnc2VhX3Jlc3VsdHNfYWxsJENvbG9yIDwtIGZhY3RvcihmZ3NlYV9yZXN1bHRzX2FsbCRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMC4zIiwgIlAgPCAwLjA1IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZEUiA8IDAuMDUiLCAiRkRSIDwgMC4wMDEiKSkNCg0KcGx0IDwtIGh0bWx0b29sczo6dGFnTGlzdCgpDQpjb3VudGVyID0gMQ0KZm9yKGMgaW4gdW5pcXVlKGZnc2VhX3Jlc3VsdHNfYWxsJENvbnRyYXN0KSApIHsNCiAgZmdzZWFfcmVzdWx0c19zbGljZSA9IGZnc2VhX3Jlc3VsdHNfYWxsW2Znc2VhX3Jlc3VsdHNfYWxsJENvbnRyYXN0ID09IGMsXQ0KDQogICMgIyBwaWNrIHRvcCBnZW5lcyBmb3IgZWl0aGVyIHNpZGUgb2Ygdm9sY2FubyB0byBsYWJlbA0KICAjICMgb3JkZXIgZ2VuZXMgZm9yIGNvbnZlbmllbmNlOg0KICBmZ3NlYV9yZXN1bHRzX3NsaWNlJGludmVydF9QIDwtICgtbG9nMTAoZmdzZWFfcmVzdWx0c19zbGljZSRwdmFsKSkgKiBzaWduKGZnc2VhX3Jlc3VsdHNfc2xpY2UkRVMpDQogIA0KICAjICNsb29wIGhlcmUgb3ZlciB0ZXN0ZWQgY29uZGl0aW9ucyBpZiBhcHBsaWNhYmxlDQogICN0b3BfZmdzZWFfZyA8LSBjKCkNCiAgdG9wX2Znc2VhX2cgPC0gYyhmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ3BhdGh3YXknXVsNCiAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyKGZnc2VhX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MTVdXSwNCiAgICAgICAgICAgICAgICAgICAgZmdzZWFfcmVzdWx0c19zbGljZVssICdwYXRod2F5J11bDQogICAgICAgICAgICAgICAgICAgICAgICBvcmRlcihmZ3NlYV9yZXN1bHRzX3NsaWNlWywgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KICAgDQogICMgZm9yIChjZWxsdHlwZSBpbiB1bmlxdWUobG1tX3Jlc3VsdHMkU3Vic2V0KSkgew0KICAjICAgICAgdG9wX2Znc2VhX2cgPC0gYyh0b3BfZmdzZWFfZywNCiAgIyAgICAgICAgICAgICAgICAgIGZnc2VhX3Jlc3VsdHNfc2xpY2VbZmdzZWFfcmVzdWx0c19zbGljZSRTdWJzZXQ9PWNlbGx0eXBlLCAncGF0aHdheSddWw0KICAjICAgICAgICAgICAgICAgICAgICAgIG9yZGVyKGZnc2VhX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IFRSVUUpWzE6MjBdXSwNCiAgIyAgICAgICAgICAgICAgICAgIGZnc2VhX3Jlc3VsdHNfc2xpY2VbZmdzZWFfcmVzdWx0c19zbGljZSRTdWJzZXQ9PWNlbGx0eXBlLCAncGF0aHdheSddWw0KICAjICAgICAgICAgICAgICAgICAgICAgIG9yZGVyKGZnc2VhX3Jlc3VsdHNfc2xpY2VbLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjIwXV0pDQogICMgfQ0KICANCiAgdG9wX2Znc2VhX2cgPC0gdW5pcXVlKHRvcF9mZ3NlYV9nKQ0KICAjZmdzZWFfcmVzdWx0c19zbGljZSA8LSBmZ3NlYV9yZXN1bHRzX3NsaWNlWywgLTEqbmNvbChmZ3NlYV9yZXN1bHRzX3NsaWNlKV0gIyByZW1vdmUgaW52ZXJ0X1AgZnJvbSBtYXRyaXgNCiAgDQogICMgR3JhcGggcmVzdWx0cw0KICBkeW5wbG90IDwtIGdncGxvdChmZ3NlYV9yZXN1bHRzX3NsaWNlLA0KICAgICAgICAgYWVzKHggPSBFUywgeSA9IC1sb2cxMChwdmFsKSwNCiAgICAgICAgICAgICBjb2xvciA9IENvbG9yLCBsYWJlbCA9IHBhdGh3YXkpKSArDQogICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKGZjX3RocmVzaG9sZCwtZmNfdGhyZXNob2xkKSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMCgwLjA1KSwgbHR5ID0gImRhc2hlZCIpICsNCiAgICAgIGdlb21fcG9pbnQoKSArDQogICAgICBsYWJzKHggPSBwYXN0ZShjLCAiIEZDIiksDQogICAgICAgICAgIHkgPSAiU2lnbmlmaWNhbmNlLCAtbG9nMTAoUCkiLA0KICAgICAgICAgICBjb2xvciA9ICJTaWduaWZpY2FuY2UiKSArDQogICAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYyhgRkRSIDwgMC4wMDFgID0gImRvZGdlcmJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYEZEUiA8IDAuMDVgID0gImxpZ2h0Ymx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgUCA8IDAuMDVgID0gIm9yYW5nZTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYE5TIG9yIEZDIDwgMC4zYCA9ICJncmF5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNCkpKSArDQogICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsMC4wNSkpKSArDQogICAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IHN1YnNldChmZ3NlYV9yZXN1bHRzX3NsaWNlLCBDb2xvciA9PSAiUCA8IDAuMDUiIHwgQ29sb3IgPT0gIkZEUiA8IDAuMDUiIHwgQ29sb3IgPT0gIkZEUiA8IDAuMDAxIiksDQogICAgICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gMC4xNSwgY29sb3IgPSAiYmxhY2siLHNpemU9NSwNCiAgICAgICAgICAgICAgICAgICAgIG1pbi5zZWdtZW50Lmxlbmd0aCA9IC4xLCBib3gucGFkZGluZyA9IC4yLCBsd2QgPSAyLA0KICAgICAgICAgICAgICAgICAgICAgbWF4Lm92ZXJsYXBzID0gNTApICsNCiAgICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE2KSArDQogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgIysNCiAgICAgICNmYWNldF93cmFwKH5TdWJzZXQsIHNjYWxlcyA9ICJmcmVlX3kiKQ0KICAgDQogIHBsdFtbY291bnRlcl1dIDwtIGFzX3dpZGdldChnZ3Bsb3RseShkeW5wbG90KSkNCiAgY291bnRlciA8LSBjb3VudGVyICsgMQ0KICAjZGF0YXRhYmxlKHN1YnNldChyZXN1bHRzLCBHZW5lICVpbiUgR09JKSwgcm93bmFtZXM9RkFMU0UsY2FwdGlvbiA9IHBhc3RlKCJERSByZXN1bHRzICIsIGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKSkNCn0NCnBsdA0KYGBgDQoNCiMjIEJhcnBsb3QgcGF0aHdheXMNCg0KQ2xlYXIgdml6dWFsaXphdGlvbiBvZiB0aGUgdG9wIDE1IHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBleHByZXNzZWQgUmVhY3RvbWUgcGF0aHdheXMuIFBhdGh3YXlzIHdpdGggYSBhZGp1c3RlZCBwLXZhbHVlIG9mIDAuMDUgb3IgaGlnaGVyIHdpbGwgYmUgcHJlc2VudGVkIGFzIHJlZCANCmFuZCBiZWxvdyAwLjA1IGFzIGdyZWVuLg0KDQpGZ3NlYSByZXR1cm5zIHJlc3VsdHMgZnJvbSB0aGUgZ2l2ZW4gZGF0YWJhc2VzLiBUbyBhdm9pZCBvbmUgZGF0YWJhc2Ugb3ZlcnNoYWRvd2luZyB0aGUgb3RoZXIsIHJlc3VsdHMgaGF2ZSBiZWVuIHNwbGl0IHRvIHNob3cgZGF0YWJhc2Ugc3BlY2lmaWMgcmVzdWx0cy4gDQpSZWFjdG9tZSBpcyBzaG93biBpbiBiYXJwbG90cyBhbmQgS0VHRyBhcyBhIHBhdGh3YXkgaW1hZ2UgaW5jbHVkaW5nIGhpZ2hsaWdodGVkIGdlbmVzLiANCg0KYGBge3IgaG9yaXpvbnRhbCBwYXRod2F5IGJhciBwbG90LCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0yMH0NCnRvcF9hbGwgPC0gYygpDQpmb3IgKGNvbnRyYXN0IGluIHVuaXF1ZShmZ3NlYV9yZWFjdG9tZSRDb250cmFzdCkpIHsNCiAgICAjIGFjdGl2ZV9ncm91cDEgPC0gY29udHJhc3RfbGlzdFtjb250cmFzdF1bWzFdXVtbMV1dDQogICAgIyBhY3RpdmVfZ3JvdXAyIDwtIGNvbnRyYXN0X2xpc3RbY29udHJhc3RdW1sxXV1bWzJdXQ0KICAgICMgaW5kIDwtIHBEYXRhKHRhcmdldF9EYXRhKVtwRGF0YSh0YXJnZXRfRGF0YSkkQU5OMiA9PSBhY3RpdmVfZ3JvdXAxIHwgcERhdGEodGFyZ2V0X0RhdGEpJEFOTjIgPT0gYWN0aXZlX2dyb3VwMl0NCiAgICB0b3AgPC0gZmdzZWFfcmVhY3RvbWVbZmdzZWFfcmVhY3RvbWUkQ29udHJhc3QgPT0gY29udHJhc3RdDQogICAgdG9wcG9zIDwtIHRvcFtvcmRlcih0b3AkTkVTLCBkZWNyZWFzaW5nID0gVFJVRSldWzA6MTVdDQogICAgdG9wbmVnIDwtIHRvcFtvcmRlcih0b3AkTkVTLCBkZWNyZWFzaW5nID0gRkFMU0UpXVswOjE1XQ0KICAgIHRvcCA8LSByYmluZCh0b3Bwb3MsIHRvcG5lZykNCiAgICANCiAgICB0b3AkQ29sb3JbdG9wJHBhZGogPCAwLjA1XSA8LSAicGFkaiA8IDAuMDUiDQogICAgdG9wJENvbG9yW3RvcCRwYWRqID09IDAuMDUgfCB0b3AkcGFkaiA+IDAuMDVdIDwtICJwYWRqID4gMC4wNSINCiAgICB0b3BfYWxsIDwtIHJiaW5kKHRvcF9hbGwsIHRvcCkNCiAgICBiYXJwbG90IDwtIGdncGxvdCh0b3AsYWVzKHg9cmVvcmRlcihwYXRod2F5LCBORVMpLHk9TkVTLGZpbGw9Q29sb3IpKSArIA0KICAgICAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDEwMCkpICsgDQogICAgICBnZW9tX2NvbChwb3NpdGlvbj0iZG9kZ2UiKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiMyRUNDNzEiLCAiI0U3NEMzQyIpKSArDQogICAgICBnZ3RpdGxlKHBhc3RlKCJUb3AgMTUgKyBhbmQgLSBORVMgcmVhY3RvbWUgcGF0aHdheXMgZnJvbSIsIGNvbnRyYXN0KSkgKyANCiAgICAgIHhsYWIoInBhdGh3YXkiKSArIHlsYWIoIk5vcm1hbGl6ZWQgRW5yaWNobWVudCBTY29yZSIpICsNCiAgICAgIGNvb3JkX2ZsaXAoKSArIA0KICAgICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArDQogICAgICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQ0KICAgICAgI2ZhY2V0X3dyYXAofkNvbnRyYXN0LCBzY2FsZXMgPSAiZnJlZV95IikNCiAgICBwcmludChiYXJwbG90KQ0KfQ0KDQpgYGANCg0KIyA4LjYgVmlzdWFsaXplIEtFR0cgcGF0aHdheQ0KDQpHZXQgcGF0aHdheSB2aXN1YWxpemF0aW9uIGZyb20gS0VHRy4gY2x1c3RlclByb2ZpbGVyIGdyYWJzIHRoZSBLRUdHIHBhdGh3YXkgSURzIGFuZCBjb21iaW5lZCB3aXRoIHBhdGh2aWV3IGl0IGNhbiBkb3dubG9hZCBhIHBhdGh3YXkgaW1hZ2UgZnJvbSB0aGUgS0VHRyBkYXRhYmFzZS4NClRoZSBMTU0gcmVzdWx0cyBhcmUgZ2l2ZW4gd2l0aCB0aGUgcGF0aHdheSBpZCB0byB2aXN1YWxpemUgdXAgYW5kIGRvd24gcmVndWxhdGVkIGdlbmVzLiBJdCB3aWxsIHBpY2sgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgcGF0aHdheSBpZiBub3QgbWFudWFsbHkgc3BlY2lmaWVkLg0KDQpgYGB7cn0NCmdldF93cF9nbXRmaWxlIDwtIGZ1bmN0aW9uKCkgew0KICAgIHdwdXJsIDwtICdodHRwczovL3dpa2lwYXRod2F5cy1kYXRhLndtY2xvdWQub3JnL2N1cnJlbnQvZ210LycNCiAgICB4IDwtIHJlYWRMaW5lcyh3cHVybCkNCiAgICB5IDwtIHhbZ3JlcCgnXFwuZ210Jyx4KV0NCiAgICBzdWIoIi4qKHdpa2lwYXRod2F5cy0uKlxcLmdtdCkuKiIsICJcXDEiLCAgeVtncmVwKCdGaWxlJywgeSldKQ0KfQ0KDQpnZXRfd3BfZGF0YSA8LSBmdW5jdGlvbihvcmdhbmlzbSkgew0KICAgIG9yZ2FuaXNtIDwtIHN1YigiICIsICJfIiwgb3JnYW5pc20pDQogICAgZ210ZmlsZSA8LSBnZXRfd3BfZ210ZmlsZSgpDQogICAgd3B1cmwgPC0gJ2h0dHBzOi8vd2lraXBhdGh3YXlzLWRhdGEud21jbG91ZC5vcmcvY3VycmVudC9nbXQvJw0KICAgIHVybCA8LSBwYXN0ZTAod3B1cmwsDQogICAgICAgICAgICAgICAgICBnbXRmaWxlW2dyZXAob3JnYW5pc20sIGdtdGZpbGUpXSkNCiAgICBmIDwtIHRlbXBmaWxlKGZpbGVleHQgPSAiLmdtdCIpDQogICAgZGwgPC0gbXlkb3dubG9hZCh1cmwsIGRlc3RmaWxlID0gZikNCiAgICBpZiAoaXMubnVsbChmKSkgew0KICAgICAgICBtZXNzYWdlKCJmYWlsIHRvIGRvd25sb2FkIHdpa2lQYXRod2F5cyBkYXRhLi4uIikNCiAgICAgICAgcmV0dXJuKE5VTEwpDQogICAgfQ0KICAgIHJlYWQuZ210LndwKGYpDQp9DQpgYGANCg0KDQpgYGB7ciBnZXQgb25saW5lIGF2YWlsYWJsZSBwYXRod2F5cywgZmlnLndpZHRoPTIwLGZpZy5oZWlnaHQ9MTB9DQojIGVucmljaEtFR0cgZ3JhYiBvbmxpbmUgcGF0aHdheXMNCmdlbmUgPC0gIHRhcmdldF9EYXRhQGZlYXR1cmVEYXRhQGRhdGFbWyJHZW5lSUQiXV0NCmtrIDwtIGVucmljaEtFR0coZ2VuZSAgICAgICAgID0gZ2VuZSwNCiAgICAgICAgICAgICAgICAgb3JnYW5pc20gICAgID0gJ2hzYScsDQogICAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMSkNCg0KZW5yaWNoS0VHR19yZXN1bHRzIDwtIGtrQHJlc3VsdA0KDQojIE1hdGNoIGNvbHVtbiB2YWx1ZXMgZm9yIGxpbmsNCiMgZnNnZWEgcnVuDQpmZ3NlYV9yZXN1bHRzJERlc2NyaXB0aW9uIDwtIGdzdWIoIktFR0dfIiwgIiIsIGZnc2VhX3Jlc3VsdHMkcGF0aHdheSkNCmZnc2VhX3Jlc3VsdHMkRGVzY3JpcHRpb24gPC0gZ3N1YigiXyIsICIgIiwgZmdzZWFfcmVzdWx0cyREZXNjcmlwdGlvbikNCiMgc3NnZWEgcnVuDQojIGxtbV9zc2dzZWFfcmVzdWx0c19kJERlc2NyaXB0aW9uIDwtIGdzdWIoIktFR0dfIiwgIiIsIGxtbV9zc2dzZWFfcmVzdWx0c19kJFBhdGh3YXkpDQojIGxtbV9zc2dzZWFfcmVzdWx0c19kJERlc2NyaXB0aW9uIDwtIGdzdWIoIl8iLCAiICIsIGxtbV9zc2dzZWFfcmVzdWx0c19kJERlc2NyaXB0aW9uKQ0KDQplbnJpY2hLRUdHX3Jlc3VsdHMkRGVzY3JpcHRpb24gPC0gdG91cHBlcihlbnJpY2hLRUdHX3Jlc3VsdHMkRGVzY3JpcHRpb24pDQoNCiMgQ3JlYXRlIGRmIGZvciBtYXRjaGluZyBLRUdHIHJlc3VsdHMNCktFR0dJRHMgPC0gZmdzZWFfcmVzdWx0cyNbZmdzZWFfcmVzdWx0cyRDb250cmFzdCA9PSAiUHJvX1R1YiAtIERpc19UdWIiXQ0KS0VHR0lEcyA8LSBLRUdHSURzICU+JSBpbm5lcl9qb2luKGVucmljaEtFR0dfcmVzdWx0cywgYnkgPSAnRGVzY3JpcHRpb24nKSAlPiUgc2VsZWN0KERlc2NyaXB0aW9uLCBJRCwgZ2VuZUlELCBsZWFkaW5nRWRnZSwgcGFkaikNCg0KIyBDcmVhdGUgZ2VuZUxpc3Qgd2l0aCBERSByZXN1bHRzIGZvciB2aXN1YWxpemF0aW9uIHBhdGh3YXkgZ2VuZXMgDQpnZW5lbGlzdCA8LSBsbW1fcmVzdWx0cyRFc3RpbWF0ZQ0KbmFtZXMoZ2VuZWxpc3QpIDwtIHRhcmdldF9EYXRhQGZlYXR1cmVEYXRhQGRhdGFbWyJHZW5lSUQiXV0NCg0KIyBDaG9vc2UgbWFudWFsIG9yIGhpZ2hlc3Qgc2lnbmlmaWNhbnQgcGF0aHdheQ0KcGF0aHdheV9uYW1lIDwtICJJTlNVTElOIFNJR05BTElORyBQQVRIV0FZIiAjIG1hbnVhbCBwYXRod2F5IHF1ZXJ5DQpwYXRod2F5X25hbWUgPC0gdG91cHBlcihwYXRod2F5X25hbWUpDQppZiAocGF0aHdheV9uYW1lID09ICIiKSB7DQogIHBhdGh3YXlfbmFtZSA8LSBLRUdHSURzW29yZGVyKEtFR0dJRHMkcGFkaiwgZGVjcmVhc2luZyA9IEZBTFNFKSwgXVsxXSREZXNjcmlwdGlvbg0KfQ0KcGF0aHdheWlkIDwtIEtFR0dJRHMkSURbS0VHR0lEcyREZXNjcmlwdGlvbiA9PSBwYXRod2F5X25hbWVdDQoNCiMgR3JhYiBwYXRod2F5IGltYWdlIHdpdGggZ2VuZSBpbmZvLCBzYXZlIGFuZCBwbG90DQp2aWV3UGF0aCA8LSBwYXRodmlldyhnZW5lLmRhdGEgID0gZ2VuZWxpc3QsDQogICAgICAgICAgICAgICAgICAgICBwYXRod2F5LmlkID0gcGF0aHdheWlkLA0KICAgICAgICAgICAgICAgICAgICAgc3BlY2llcyAgICA9ICJoc2EiLA0KICAgICAgICAgICAgICAgICAgICAgb3V0LnN1ZmZpeCA9ICJnZW5lc2luZm8iLCBrZWdnLm5hdGl2ZSA9IFQsIHNhbWUubGF5ZXIgPSBGKQ0KaW1nIDwtIHJlYWRQTkcocGFzdGUocGF0aHdheWlkLCAiLmdlbmVzaW5mby5wbmciLCBzZXAgPSAiIikpDQpncmlkOjpncmlkLnJhc3RlcihpbWcpDQpgYGANCg0KIyA5IFNwYXRpYWwgRGVjb252b2x1dGlvbg0KDQojIyA5LjEgQ2FsY3VsYXRlIGJhY2tncm91bmRzDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25fYmd9DQojZ2MoKQ0KYmcgPSBkZXJpdmVfR2VvTXhfYmFja2dyb3VuZCgNCiAgbm9ybSA9IGFzc2F5RGF0YUVsZW1lbnQodGFyZ2V0X0RhdGEgLCBlbHQgPSAicV9ub3JtIiksDQogIHByb2JlcG9vbCA9IGZEYXRhKHRhcmdldF9EYXRhKSRNb2R1bGUsDQogIG5lZ25hbWVzID0gYygiTmVnUHJvYmUtQ1RQMDEiLCJOZWdQcm9iZS1LaWxvIiwiTmVnYXRpdmUgUHJvYmUiLCAiTmVnUHJvYmUtV1RYIiApKQ0KICAjbmVnbmFtZXMgPSAiTmVnUHJvYmUtV1RYIikNCg0KYGBgDQoNCiMjIDkuMiBMb2FkIGNlbGwgcHJvZmlsZQ0KDQpBICJjZWxsIHByb2ZpbGUgbWF0cml4IiBpcyBhIHByZS1kZWZpbmVkIG1hdHJpeCB0aGF0IHNwZWNpZmllcyB0aGUNCmV4cGVjdGVkIGV4cHJlc3Npb24gcHJvZmlsZXMgb2YgZWFjaCBjZWxsIHR5cGUgaW4gdGhlIGV4cGVyaW1lbnQuIFRoZQ0KU3BhdGlhbERlY29uIGxpYnJhcnkgY29tZXMgd2l0aCBvbmUgc3VjaCBtYXRyaXggcHJlLWxvYWRlZCwgdGhlDQoiU2FmZVRNRSIgbWF0cml4LCBkZXNpZ25lZCBmb3IgZXN0aW1hdGlvbiBvZiBpbW11bmUgYW5kIHN0cm9tYSBjZWxscyBpbg0KdGhlIHR1bW9yIG1pY3JvZW52aXJvbm1lbnQuIChUaGlzIG1hdHJpeCB3YXMgZGVzaWduZWQgdG8gYXZvaWQgZ2VuZXMNCmNvbW1vbmx5IGV4cHJlc3NlZCBieSBjYW5jZXIgY2VsbHM7IHNlZSB0aGUgU3BhdGlhbERlY29uIG1hbnVzY3JpcHQgZm9yDQpkZXRhaWxzLikuIE90aGVyd2lzZSwgbG9hZCBzcGVjaWZpYyBwcm9maWxlcyBmcm9tDQo8aHR0cHM6Ly9naXRodWIuY29tL05hbm9zdHJpbmctQmlvc3RhdHMvQ2VsbFByb2ZpbGVMaWJyYXJ5L3RyZWUvTmV3UHJvZmlsZU1hdHJpY2VzPg0KDQpgYGB7ciBsb2FkX2NlbGxfcHJvZmlsZXN9DQojc2FmZVRNRQ0KZGF0YSgic2FmZVRNRSIpDQpkYXRhKCJzYWZlVE1FLm1hdGNoZXMiKQ0KY3VycmVudF9jZWxsX3Byb2ZpbGU8LXNhZmVUTUUNCg0KI3NlZTogaHR0cHM6Ly9naXRodWIuY29tL05hbm9zdHJpbmctQmlvc3RhdHMvQ2VsbFByb2ZpbGVMaWJyYXJ5L3RyZWUvTmV3UHJvZmlsZU1hdHJpY2VzDQoNCmN1cnJlbnRfY2VsbF9wcm9maWxlIDwtIGRvd25sb2FkX3Byb2ZpbGVfbWF0cml4KHNwZWNpZXMgPSAiSHVtYW4iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZ2VfZ3JvdXAgPSAiQWR1bHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXRyaXhuYW1lID0gIktpZG5leV9IQ0EiKQ0KDQpoZWF0bWFwKHN3ZWVwKGN1cnJlbnRfY2VsbF9wcm9maWxlLCAxLCBhcHBseShjdXJyZW50X2NlbGxfcHJvZmlsZSwgMSwgbWF4KSwgIi8iKSwNCiAgICAgICAgbGFiUm93ID0gTkEsIG1hcmdpbnMgPSBjKDEwLCA1KSwgY2V4Q29sID0gMC43KQ0KYGBgDQoNCiMgOS4zIFJ1biBzcGF0aWFsIGRlY29udm9sdXRpb24NCg0KYGBge3Igc3BhdGlhbF9kZWNvbl9ydW59DQojIHZlY3RvciBpZGVudGlmeWluZyBwdXJlIHR1bW9yIHNlZ21lbnRzOg0KI3RhcmdldF9EYXRhJGlzdHVtb3IgPSB0YXJnZXRfRGF0YSRBTk4zID09ICJDT1JFIiAmIHRhcmdldF9EYXRhJEFOTjEgPT0gIlBhbkNLKyINCnJlcyA9IHJ1bnNwYXRpYWxkZWNvbihvYmplY3QgPSB0YXJnZXRfRGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICBub3JtX2VsdCA9ICJxX25vcm0iLA0KICAgICAgICAgICAgICAgICAgICAgIHJhd19lbHQgPSAiZXhwcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICNpc19wdXJlX3R1bW9yID0gdGFyZ2V0X0RhdGEkaXN0dW1vciwNCiAgICAgICAgICAgICAgICAgICAgICBjZWxsX2NvdW50cyA9IHRhcmdldF9EYXRhJG51Y2xlaSwNCiAgICAgICAgICAgICAgICAgICAgICBYID0gY3VycmVudF9jZWxsX3Byb2ZpbGUsDQogICAgICAgICAgICAgICAgICAgICAgI2NlbGxtZXJnZXMgPSBzYWZlVE1FLm1hdGNoZXMsICAgICAgICAgICAgICAjIHNhZmVUTUUubWF0Y2hlcyBvYmplY3QsIHVzZWQgYnkgZGVmYXVsdA0KICAgICAgICAgICAgICAgICAgICAgICNuX3R1bW9yX2NsdXN0ZXJzID0gNSwgICAgICAgICAgICAgICAgICAgICAgIyBob3cgbWFueSBkaXN0aW5jdCB0dW1vciBwcm9maWxlcyB0byBhcHBlbmQgdG8gc2FmZVRNRQ0KICAgICAgICAgICAgICAgICAgICAgIGFsaWduX2dlbmVzID0gVFJVRSkNCg0KDQpgYGANCg0KIyA5LjMuMSBTcGF0aWFsIGRlY29udm9sdXRpb24gaGVhdG1hcHMgey50YWJzZXQgLnRhYnNldC1waWxsc30NCg0KIyMgQWJ1bmRhbmNlDQoNCmBgYHtyIHNwYXRpYWxfZGVjb25faGVhdG1hcCwgZmlnLndpZHRoPTI1LGZpZy5oZWlnaHQ9MTV9DQojIE5PVEU6IGNoZWNrIGNsdXN0ZXJpbmcuLiB3aHkgZGlmZmVyZW50Pw0KDQojc2V0IGRpc3BsYXkgdGhyZXNob2xkcw0KdGhyZXNoIDwtIHNpZ25pZihxdWFudGlsZShyZXMkYmV0YSwgMC45NyksIDIpDQoNCiMgcGxvdCBzdG9yZWQgdG8ga2VlcCBjbHVzdGVyaW5nIGZvciBsYXRlcg0KcDE8LXBoZWF0bWFwKHBtaW4odChyZXMkYmV0YSksdGhyZXNoKSwNCiAgICAgICAgICNzY2FsZSA9ICJyb3ciLCANCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMywNCiAgICAgICAgIGN1dHJlZV9yb3dzID0gMiwNCiAgICAgICAgIGZvbnRzaXplX3JvdyA9IDEyLA0KICAgICAgICAgc2hvd19yb3duYW1lcyA9IFRSVUUsIHNob3dfY29sbmFtZXMgPSBUUlVFLA0KICAgICAgICAgYW5nbGVfY29sID0gIjkwIiwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGxlZ2VuZF9icmVha3MgPSBjKHJvdW5kKHNlcSgwLCB0aHJlc2gsIGxlbmd0aC5vdXQgPSA1KSlbLTVdLCB0aHJlc2gpLA0KICAgICAgICAgbGVnZW5kX2xhYmVscyA9IGMocm91bmQoc2VxKDAsIHRocmVzaCwgbGVuZ3RoLm91dCA9IDUpKVstNV0sIHBhc3RlMCgiQWJ1bmRhbmNlIHNjb3JlcyxcbnRydW5jYXRlZCBhYm92ZSBhdCAiLCB0aHJlc2gpKSwNCiAgICAgICAgICNicmVha3MgPSBzZXEoMCwgNSwgMSksDQogICAgICAgICBjb2xvciA9IGNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCJkYXJrYmx1ZSIpKSgxMDApLA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBjb2xvcl9saXN0LA0KICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBwRGF0YSh0YXJnZXRfRGF0YSlbLCBhbm5fbmFtZXNdDQogICAgICAgICApDQojcDENCmBgYA0KDQojIyBQcm9wb3J0aW9uYWwNCg0KYGBge3Igc3BhdGlhbF9kZWNvbl9wcm9waGVhdG1hcCwgZmlnLndpZHRoPTI1LGZpZy5oZWlnaHQ9MTV9DQojIHByb3BvcnRpb25zOg0KcHJvcHMgPC0gcmVwbGFjZShyZXMkcHJvcF9vZl9ub250dW1vciwgaXMubmEocmVzJHByb3Bfb2Zfbm9udHVtb3IpLCAwKQ0KDQpwMjwtcGhlYXRtYXAodChwcm9wcyksDQogICAgICAgICAjc2NhbGUgPSAicm93IiwgDQogICAgICAgICBjdXRyZWVfY29scyA9IDMsDQogICAgICAgICBjdXRyZWVfcm93cyA9IDIsDQogICAgICAgICBmb250c2l6ZV9yb3cgPSAxMiwNCiAgICAgICAgIHNob3dfcm93bmFtZXMgPSBUUlVFLCBzaG93X2NvbG5hbWVzID0gVFJVRSwNCiAgICAgICAgIGFuZ2xlX2NvbCA9ICI5MCIsDQogICAgICAgICBib3JkZXJfY29sb3IgPSBOQSwNCiAgICAgICAgICNjbHVzdGVyaW5nX21ldGhvZCA9ICJhdmVyYWdlIiwNCiAgICAgICAgICNjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSAiY29ycmVsYXRpb24iLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICBsZWdlbmRfYnJlYWtzID0gcm91bmQoc2VxKDAsIG1heChwcm9wcykgKiAwLjk5LCBsZW5ndGgub3V0ID0gNSksIDIpLA0KICAgICAgICAgbGVnZW5kX2xhYmVscyA9IGMocm91bmQoc2VxKDAsIG1heChwcm9wcyksIGxlbmd0aC5vdXQgPSA1KSwgMilbLTVdLCAiUHJvcG9ydGlvbiBvZiBhbGxcbmZpdHRlZCBwb3B1bGF0aW9ucyIpLA0KICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoIndoaXRlIiwiZGFya2JsdWUiKSkoMTAwKSwNCiAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gY29sb3JfbGlzdCwNCiAgICAgICAgIGFubm90YXRpb25fY29sID0gcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX25hbWVzXSkNCg0KI3AyDQoNCmBgYA0KDQojIyBTY2FsZWQNCg0KYGBge3Igc3BhdGlhbF9kZWNvbl9zY2FsZWRoZWF0bWFwLCBmaWcud2lkdGg9MjUsZmlnLmhlaWdodD0xNX0NCiMgc2NhbGVkIGFidW5kYW5jZXM6DQplcHNpbG9uIDwtIG1pbihyZXMkYmV0YVtyZXMkYmV0YSA+IDBdKQ0KbWF0IDwtIHN3ZWVwKHJlcyRiZXRhLCAxLCBwbWF4KGFwcGx5KHJlcyRiZXRhLCAxLCBtYXgpLCBlcHNpbG9uKSwgIi8iKQ0KDQpwaGVhdG1hcCh0KG1hdCksDQogICAgICAgICAjc2NhbGUgPSAicm93IiwNCiAgICAgICAgIGN1dHJlZV9jb2xzID0gMywNCiAgICAgICAgIGN1dHJlZV9yb3dzID0gMywNCiAgICAgICAgIGZvbnRzaXplX3JvdyA9IDEyLA0KICAgICAgICAgc2hvd19yb3duYW1lcyA9IFRSVUUsIHNob3dfY29sbmFtZXMgPSBUUlVFLA0KICAgICAgICAgYW5nbGVfY29sID0gIjkwIiwNCiAgICAgICAgIGJvcmRlcl9jb2xvciA9IE5BLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfbWV0aG9kID0gImF2ZXJhZ2UiLA0KICAgICAgICAgI2NsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJjb3JyZWxhdGlvbiIsDQogICAgICAgICAjY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNvcnJlbGF0aW9uIiwNCiAgICAgICAgIGxlZ2VuZF9icmVha3MgPSBjKHJvdW5kKHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gNSksIDIpWy01XSwgMSksDQogICAgICAgICBsZWdlbmRfbGFiZWxzID0gYyhyb3VuZChzZXEoMCwgMSwgbGVuZ3RoLm91dCA9IDUpLCAyKVstNV0sICJTY2FsZWQgYWJ1bmRhbmNlXG4ocmF0aW8gdG8gbWF4KSIpLA0KICAgICAgICAgY29sb3IgPSBjb2xvclJhbXBQYWxldHRlKGMoIndoaXRlIiwiZGFya2JsdWUiKSkoMTAwKSwNCiAgICAgICAgIGFubm90YXRpb25fY29sb3JzID0gY29sb3JfbGlzdCwNCiAgICAgICAgIGFubm90YXRpb25fY29sID0gcERhdGEodGFyZ2V0X0RhdGEpWywgYW5uX25hbWVzXSkNCg0KYGBgDQoNCiMgOS40IEJhcnBsb3RzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9DQoNCiMjIGFidW5kYW5jZQ0KDQpgYGB7ciBTRF9hYnVuZGFuY2VfYmFycGxvdCwgZmlnLndpZHRoPTI1LGZpZy5oZWlnaHQ9MTV9DQojIGRlZmluZSB2YXJpYWJsZXMgdG8gc2hvdyBpbiBoZWF0bWFwczoNCnBEYXRhKHRhcmdldF9EYXRhKSRyZWdpb24gPC0gDQogICAgZmFjdG9yKHBEYXRhKHRhcmdldF9EYXRhKSRBTk4zLCB1bmlxdWUodGFyZ2V0X0RhdGEkQU5OMykpICAgIyBNdXN0IGJlIG1hbnVhbD8NCnBEYXRhKHRhcmdldF9EYXRhKSRjbGFzcyA8LSANCiAgICBmYWN0b3IocERhdGEodGFyZ2V0X0RhdGEpJEFOTjEsIHVuaXF1ZSh0YXJnZXRfRGF0YSRBTk4xKSkgICAjIE11c3QgYmUgbWFudWFsPw0KDQp2YXJpYWJsZXNfdG9fcGxvdCA8LSBjKCJzbGlkZV9uYW1lIiwgIkFOTjEiLCAiQU5OMyIpICAgICAgICAgICAgICAgICAgICAgIyBNdXN0IGJlIG1hbnVhbD8NCg0KI2RlZmluZSBjb2xvcnMNCg0KIyBvbmx5IGZvciBzYWZlVE1FIGNvbG9ycw0KI2NvbCA8LSBjZWxsY29scw0KDQojY3VzdG9tIGNlbG1hdHJpeA0KI2dldCBsYXJnZSBudW1iZXIgb2YgY29sb3JzDQpxdWFsX2NvbF9wYWxzID0gYnJld2VyLnBhbC5pbmZvW2JyZXdlci5wYWwuaW5mbyRjYXRlZ29yeSA9PSAncXVhbCcsXQ0KY29sX3ZlY3RvciA9IHVuaXF1ZSh1bmxpc3QobWFwcGx5KGJyZXdlci5wYWwsIHF1YWxfY29sX3BhbHMkbWF4Y29sb3JzLCByb3duYW1lcyhxdWFsX2NvbF9wYWxzKSkpKQ0KY2VsbHR5cGVzPC1zYW1wbGUoY29sX3ZlY3RvcixsZW5ndGgoY29sbmFtZXMocmVzJGJldGEpKSkNCm5hbWVzKGNlbGx0eXBlcyk8LWNvbG5hbWVzKHJlcyRiZXRhKQ0KY29sPC1jZWxsdHlwZXMNCg0KDQojdGVtcGZpeCBmb3IgYWJicmV2aWF0ZWQgYW5kIG5vdyBtaXNtYXRjaGluZyBhbm5vdGF0aW9ucw0KI2NhbiBqdXN0IHVzZSBhbm4gZGF0YWZyYW1lPw0KI3RtcGFubjwtY2JpbmQoQU5OMSxBTk4yLFNOKQ0KdG1wYW5uIDwtIHBEYXRhKHRhcmdldF9EYXRhKVthbm5fbmFtZXNdDQoNCg0KDQpsYXlvdXQobWF0cml4KGMoMSwgMiwgMywgMyksIG5yb3cgPSAyKSwNCiAgICAgICB3aWR0aHMgPSBjKDEwLCAzLCAxMCwgMyksDQogICAgICAgaGVpZ2h0cyA9IGMoMSwgOCwgMTApLA0KICAgICAgKQ0KDQpwYXIobWFyID0gYygwLCA4LjIsIDAsIDAuMikpDQpwbG90KHAxJHRyZWVfY29sLCBsYWJlbHMgPSBGLCBtYWluID0gIiIsIHlsYWIgPSAiIiwgeWF4dCA9ICJuIikNCnBhcihtYXIgPSBjKDE1LCA4LCAwLCAwKSkNCg0KIyBkYXRhIHRvIHBsb3Q6DQptYXQgPC0gdChyZXMkYmV0YSlbLCBwMSR0cmVlX2NvbCRvcmRlcl0NCiMgaW5mZXIgc2NhbGUgb2YgbmVnYXRpdmUgeS1heGlzIGZvciBhbm5vdGF0aW9uIGNvbG9yYmFycw0KeW1pbiA8LSAtbWF4KGNvbFN1bXMobWF0KSkgKiAwLjE1DQppZiAoIWlzLmZpbml0ZSh5bWluKSkgew0KICB5bWluIDwtIDANCn0NCg0KIyBkcmF3IGJhcnBsb3Q6DQpicCA8LSBiYXJwbG90KG1hdCwNCiAgICAgICAgICAgICAgY2V4LmxhYiA9IDEuNSwNCiAgICAgICAgICAgICAgY29sID0gY29sLCBib3JkZXIgPSBOQSwNCiAgICAgICAgICAgICAgY2V4Lm5hbWVzID0gMS4xLA0KICAgICAgICAgICAgICBsYXMgPSAyLCBtYWluID0gIiIsIHlsYWIgPSAiQWJ1bmRhbmNlIHNjb3JlcyIsDQogICAgICAgICAgICAgIHlsaW0gPSBjKHltaW4sIG1heChjb2xTdW1zKG1hdCkpKQ0KKQ0KDQoNCiMgYWRkIGNvbG9yIGJhcnMgZm9yIGFubm90YXRpb25zDQpmb3IgKG5hbWUgaW4gcmV2KHZhcmlhYmxlc190b19wbG90KSkgew0KICB5cmFuZ2UgPC0gc2VxKHltaW4gLyAzLCB5bWluLCBsZW5ndGgub3V0ID0gbGVuZ3RoKHZhcmlhYmxlc190b19wbG90KSArIDEpW21hdGNoKG5hbWUsIHZhcmlhYmxlc190b19wbG90KSArIGMoMCwgMSldDQogIHh3aWR0aCA8LSAoYnBbMl0gLSBicFsxXSkgLyAyDQogIGZvciAoaSBpbiAxOm5jb2wobWF0KSkgew0KICAgIHJlY3QoYnBbaV0gLSB4d2lkdGgsIHlyYW5nZVsyXSwgYnBbaV0gKyB4d2lkdGgsIHlyYW5nZVsxXSwNCiAgICAgICAgICMgYm9yZGVyID0gTkEsIGNvbCA9IGFubl9jb2xvcnNbW25hbWVdXVtzZWdtZW50QW5ub3RhdGlvbnNbbWF0Y2goY29sbmFtZXMobWF0KVtpXSwgc2VnbWVudEFubm90YXRpb25zJHNlZ21lbnRJRCksIG5hbWVdXQ0KICAgICAgICAgI2JvcmRlciA9IE5BLCBjb2wgPSBhbm5fY29sb3JzW1tuYW1lXV1bYW5uW3AxJHRyZWVfY29sJG9yZGVyW2ldLCBuYW1lXV0NCiAgICAgICAgIGJvcmRlciA9IE5BLCBjb2wgPSBjb2xvcl9saXN0W1tuYW1lXV1bdG1wYW5uW2NvbG5hbWVzKG1hdClbaV0sIG5hbWVdXQ0KICAgICkNCiAgfQ0KfQ0KYXhpcygyLA0KICAgICBhdCA9IHNlcSh5bWluIC8gMywgeW1pbiwgbGVuZ3RoLm91dCA9IGxlbmd0aCh2YXJpYWJsZXNfdG9fcGxvdCkgKyAyKVstYygxLCBsZW5ndGgodmFyaWFibGVzX3RvX3Bsb3QpICsgMildLA0KICAgICBsYXMgPSAyLCBsYWJlbHMgPSB2YXJpYWJsZXNfdG9fcGxvdCwgbHR5ID0gMCwgY2V4LmF4aXMgPSAxLjINCikNCg0KI2RyYXcgYSBsZWdlbmQ6DQpwYXIobWFyID0gYygwLjEsIDAuMSwgMC4xLCAwLjEpKQ0KZnJhbWUoKQ0KbGVnZW5kY29scyA8LSBsZWdlbmRuYW1lcyA8LSBjKCkNCiNmb3IgKG5hbWUgaW4gcmV2KG5hbWVzKGFubl9jb2xvcnMpKSkgew0KZm9yIChuYW1lIGluIGMoInNsaWRlX25hbWUiLCAiQU5OMSIsICJBTk4zIikpIHsNCiAgbGVnZW5kY29scyA8LSBjKGxlZ2VuZGNvbHMsIE5BLCBjb2xvcl9saXN0W1tuYW1lXV0sIE5BKQ0KICBsZWdlbmRuYW1lcyA8LSBjKGxlZ2VuZG5hbWVzLCBuYW1lLCBuYW1lcyhjb2xvcl9saXN0W1tuYW1lXV0pLCBOQSkNCn0NCmxlZ2VuZCgiY2VudGVyIiwNCiAgICAgICBwY2ggPSAxNSwNCiAgICAgICBjZXggPSAxLjUsDQogICAgICAgY29sID0gYyhsZWdlbmRjb2xzLCByZXAoTkEsIDEpLCByZXYoY29sKSksDQogICAgICAgbGVnZW5kID0gYyhsZWdlbmRuYW1lcywgIkNlbGwgdHlwZSIsIHJldihuYW1lcyhjb2wpKSksDQopDQpgYGANCg0KIyMgcHJvcG9ydGlvbmFsDQoNCmBgYHtyIFNEX3Byb3BfYmFycGxvdCwgZmlnLndpZHRoPTI1LGZpZy5oZWlnaHQ9MTV9DQojIGRlZmluZSB2YXJpYWJsZXMgdG8gc2hvdyBpbiBoZWF0bWFwczoNCnZhcmlhYmxlc190b19wbG90IDwtIGMoInNsaWRlX25hbWUiLCAiQU5OMSIsICJBTk4zIikNCg0KbGF5b3V0KG1hdHJpeChjKDEsIDIsIDMsIDMpLCBucm93ID0gMiksDQogICAgICAgd2lkdGhzID0gYygxMCwgMywgMTAsIDMpLA0KICAgICAgIGhlaWdodHMgPSBjKDEsIDgsIDEwKSwNCiAgICAgICkNCg0KcGFyKG1hciA9IGMoMCwgOC4yLCAwLCAwLjIpKQ0KcGxvdChwMiR0cmVlX2NvbCwgbGFiZWxzID0gRiwgbWFpbiA9ICIiLCB5bGFiID0gIiIsIHlheHQgPSAibiIpDQpwYXIobWFyID0gYygxNSwgOCwgMCwgMCkpDQoNCiMgZGF0YSB0byBwbG90Og0KbWF0IDwtIHQocmVzJHByb3Bfb2Zfbm9udHVtb3IpWywgcDIkdHJlZV9jb2wkb3JkZXJdDQogIG1hdCA8LSByZXBsYWNlKG1hdCwgaXMubmEobWF0KSwgMCkNCiAgIyBpbmZlciBzY2FsZSBvZiBuZWdhdGl2ZSB5LWF4aXMgZm9yIGFubm90YXRpb24gY29sb3JiYXJzDQogIHltaW4gPC0gLTAuMTUNCg0KIyBkcmF3IGJhcnBsb3Q6DQpicCA8LSBiYXJwbG90KG1hdCwNCiAgICAgICAgICAgICAgY2V4LmxhYiA9IDEuNSwNCiAgICAgICAgICAgICAgY29sID0gY29sLCBib3JkZXIgPSBOQSwNCiAgICAgICAgICAgICAgY2V4Lm5hbWVzID0gMS4xLA0KICAgICAgICAgICAgICBsYXMgPSAyLCBtYWluID0gIiIsIHlsYWIgPSAiUHJvcG9ydGlvbiBvZiBmaXR0ZWQgY2VsbHMiLA0KICAgICAgICAgICAgICB5bGltID0gYyh5bWluLCBtYXgoY29sU3VtcyhtYXQpKSkNCikNCg0KIyBhZGQgY29sb3IgYmFycyBmb3IgYW5ub3RhdGlvbnMNCmZvciAobmFtZSBpbiByZXYodmFyaWFibGVzX3RvX3Bsb3QpKSB7DQogIHlyYW5nZSA8LSBzZXEoeW1pbiAvIDMsIHltaW4sIGxlbmd0aC5vdXQgPSBsZW5ndGgodmFyaWFibGVzX3RvX3Bsb3QpICsgMSlbbWF0Y2gobmFtZSwgdmFyaWFibGVzX3RvX3Bsb3QpICsgYygwLCAxKV0NCiAgeHdpZHRoIDwtIChicFsyXSAtIGJwWzFdKSAvIDINCiAgZm9yIChpIGluIDE6bmNvbChtYXQpKSB7DQogICAgcmVjdChicFtpXSAtIHh3aWR0aCwgeXJhbmdlWzJdLCBicFtpXSArIHh3aWR0aCwgeXJhbmdlWzFdLA0KICAgICAgICAgIyBib3JkZXIgPSBOQSwgY29sID0gYW5uX2NvbG9yc1tbbmFtZV1dW3NlZ21lbnRBbm5vdGF0aW9uc1ttYXRjaChjb2xuYW1lcyhtYXQpW2ldLCBzZWdtZW50QW5ub3RhdGlvbnMkc2VnbWVudElEKSwgbmFtZV1dDQogICAgICAgICAjYm9yZGVyID0gTkEsIGNvbCA9IGFubl9jb2xvcnNbW25hbWVdXVthbm5bcDIkdHJlZV9jb2wkb3JkZXJbaV0sIG5hbWVdXQ0KICAgICAgICAgYm9yZGVyID0gTkEsIGNvbCA9IGNvbG9yX2xpc3RbW25hbWVdXVt0bXBhbm5bY29sbmFtZXMobWF0KVtpXSwgbmFtZV1dDQogICAgKQ0KICB9DQp9DQpheGlzKDIsDQogICAgIGF0ID0gc2VxKHltaW4gLyAzLCB5bWluLCBsZW5ndGgub3V0ID0gbGVuZ3RoKHZhcmlhYmxlc190b19wbG90KSArIDIpWy1jKDEsIGxlbmd0aCh2YXJpYWJsZXNfdG9fcGxvdCkgKyAyKV0sDQogICAgIGxhcyA9IDIsIGxhYmVscyA9IHZhcmlhYmxlc190b19wbG90LCBsdHkgPSAwLCBjZXguYXhpcyA9IDEuMg0KKQ0KDQoNCiNkcmF3IGEgbGVnZW5kOg0KcGFyKG1hciA9IGMoMC4xLCAwLjEsIDAuMSwgMC4xKSkNCmZyYW1lKCkNCmxlZ2VuZGNvbHMgPC0gbGVnZW5kbmFtZXMgPC0gYygpDQojZm9yIChuYW1lIGluIHJldihuYW1lcyhhbm5fY29sb3JzKSkpIHsNCmZvciAobmFtZSBpbiBjKCJzbGlkZV9uYW1lIiwgIkFOTjEiLCAiQU5OMyIpKSB7DQogIGxlZ2VuZGNvbHMgPC0gYyhsZWdlbmRjb2xzLCBOQSwgY29sb3JfbGlzdFtbbmFtZV1dLCBOQSkNCiAgbGVnZW5kbmFtZXMgPC0gYyhsZWdlbmRuYW1lcywgbmFtZSwgbmFtZXMoY29sb3JfbGlzdFtbbmFtZV1dKSwgTkEpDQp9DQpsZWdlbmQoImNlbnRlciIsDQogICAgICAgcGNoID0gMTUsDQogICAgICAgY2V4ID0gMS40LA0KICAgICAgIGNvbCA9IGMobGVnZW5kY29scywgcmVwKE5BLCAxKSwgcmV2KGNvbCkpLA0KICAgICAgIGxlZ2VuZCA9IGMobGVnZW5kbmFtZXMsICJDZWxsIHR5cGUiLCByZXYobmFtZXMoY29sKSkpLA0KKQ0KYGBgDQoNCiMjIEJveHBsb3RzDQoNCkJveHBsb3QgcGVyIGNlbGwgZnJvbSB0aGUgc3BhdGlhbCBkZWNvbnZvbHV0aW9uLiBDaG9vc2UgeW91ciBhbm5vdGF0aW9uIGluIHdoaWNoIHlvdSB3YW50IHRvIGNvbXBhcmUgKEFOTikuIA0KVGhlIGFubm90YXRpb24gd2lsbCBiZSBkaXZpZGVkIGluIGNvbmRpdGlvbnMgYW5kIGVhY2ggY2VsbCB3aXRoIGhhdmUgYSBib3hwbG90IHdoaWNoIGluY2x1ZGUgdGhlIGFidW5kYW5jZSBzY29yZSBvZiBlYWNoIGNvbmRpdGlvbi4NCkEgc3RhdGlzdGljYWwgdGVzdCBoYXMgYmVlbiBhZGRlZCBpbnNpZGUgdGhlIGJveHBsb3QgdG8gY2hlY2sgZm9yIHNpZ25pZmljYW5jZS4gRmVlbCBmcmVlIHRvIGNoYW5jZSB0aGUgdGVzdCB0byBmaXQgdGhlIGRhdGEuDQoNCnNpZ25pZmljYW5jZSBjb2RlICAgICAgICAgcC12YWx1ZQ0KICAgKioqICAgICAgICAgICAgICAgICBbMCwgMC4wMDFdDQogICAgKiogICAgICAgICAgICAgICgwLjAwMSwgMC4wMV0NCiAgICAgKiAgICAgICAgICAgICAgICgwLjAxLCAwLjA1XQ0KICAgICAuICAgICAgICAgICAgICAgICgwLjA1LCAwLjFdDQogICAgICAgICAgICAgICAgICAgICAgICAgKDAuMSwgMV0gDQoNCmBgYHtyIGZpZy5oZWlnaHQ9NDAsIGZpZy53aWR0aD00MCwgd2FybmluZz1GQUxTRX0NCkFOTiA8LSAiQU5OMSIgIyBBTk4gY29sdW1uIHdpdGggY29uZGl0aW9ucw0KaWYgKGxlbmd0aCh1bmlxdWUodGFyZ2V0X0RhdGFbW0FOTl1dKSkgPiAyKSB7DQogIHRlc3QgPC0gImtydXNrYWwudGVzdCINCn0gZWxzZSB7DQogIHRlc3QgPC0gIndpbGNveC50ZXN0Ig0KfQ0KDQpyZXNfZmlsdGVyIDwtIHJlc1shcmVzQHBoZW5vRGF0YUBkYXRhW1siYmV0YSJdXT09MF0NCnJlc19maWx0ZXIgPC0gYXMuZGF0YS5mcmFtZShyZXNfZmlsdGVyJGJldGEpDQpyZXNfZmlsdGVyW3Jlc19maWx0ZXIgPT0gMF0gPC0gTkENCmJveHBsb3RfcHJlcCA8LSByZXNfZmlsdGVyWyAsIGNvbFN1bXMoaXMubmEocmVzX2ZpbHRlcikpIDwgbnJvdyhyZXNfZmlsdGVyKV0gDQoNCmJveHBsb3RfcHJlcCRjb25kaXRpb24gPC0gcm93bmFtZXMoYm94cGxvdF9wcmVwKQ0KDQpmb3IgKHZhbHVlIGluIHVuaXF1ZSh0YXJnZXRfRGF0YVtbQU5OXV0pKSB7DQogIGNvbiA8LSByb3duYW1lcyhwRGF0YSh0YXJnZXRfRGF0YSlbcERhdGEodGFyZ2V0X0RhdGEpW1tBTk5dXSA9PSB2YWx1ZSxdKQ0KICBib3hwbG90X3ByZXAkY29uZGl0aW9uW2JveHBsb3RfcHJlcCRjb25kaXRpb24gJWluJSBjb25dIDwtIHZhbHVlDQp9DQoNCmJveHBsb3QgPC0gbWVsdChib3hwbG90X3ByZXApDQoNCiMgZnVuY3Rpb24gZm9yIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgDQpnaXZlLm4gPC0gZnVuY3Rpb24oeCl7DQogIHJldHVybihkYXRhLmZyYW1lKHkgPSAtMSwgbGFiZWwgPSBwYXN0ZTAoIm4gPSAiLGxlbmd0aCh4KSkpKQ0KICNyZXR1cm4oYyh5ID0gbWVkaWFuKHgpKjEuMDUsIGxhYmVsID0gbGVuZ3RoKHgpKQ0KIyBleHBlcmltZW50IHdpdGggdGhlIG11bHRpcGxpZXIgdG8gZmluZCB0aGUgcGVyZmVjdCBwb3NpdGlvbg0KfQ0KDQpnZ3Bsb3QoYm94cGxvdCwgYWVzKGZhY3Rvcihjb25kaXRpb24pLCB2YWx1ZSwgZmlsbCA9IGNvbmRpdGlvbikpICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGdlb21faml0dGVyKHBvc2l0aW9uPXBvc2l0aW9uX2ppdHRlcih3PTAuMSxoPTAuMSkpICsNCiAgZmFjZXRfd3JhcCh+dmFyaWFibGUsIHNjYWxlPSJmcmVlIiwgbmNvbCA9IDUpICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApKSArDQogIHJvdGF0ZV94X3RleHQoYW5nbGUgPSA0NSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlNwYXRpYWwgRGVjb252b2x1dGlvbiIsDQogICAgeCA9ICJDb25kaXRpb25zIiwNCiAgICB5ID0gIkFidW5jYW5jZSBzY29yZSIpICsgDQogIHN0YXRfY29tcGFyZV9tZWFucyhtZXRob2QgPSB0ZXN0LCBsYWJlbC55ID0gLTMsIHNpemU9NSkgKyANCiAgc3RhdF9jb21wYXJlX21lYW5zKGxhYmVsID0gInAuc2lnbmlmIiwgbWV0aG9kID0gInQudGVzdCIsDQogICAgICAgICAgICAgICAgICAgICByZWYuZ3JvdXAgPSAiLmFsbC4iLCBoaWRlLm5zID0gVFJVRSwgc2l6ZT01KSArDQogIHN0YXRfc3VtbWFyeShmdW4uZGF0YSA9IGdpdmUubiwgZ2VvbSA9ICJ0ZXh0Iiwgc2l6ZSA9IDUpDQoNCg0KI3N0YXRfY29tcGFyZV9tZWFucyhzaXplPTUpDQpgYGANCg0KDQojIDExIENOVg0KQ29weSBOdW1iZXIgVmFyaWF0aW9uIGFuYWx5c2lzIHdpdGggdGhlIFIgc29mdHdhcmUgcGFja2FnZSBpbmZlckNOViAoaHR0cHM6Ly9naXRodWIuY29tL2Jyb2FkaW5zdGl0dXRlL2luZmVyQ05WL3dpa2kpLg0KDQpJbmZlckNOViBpcyB1c2VkIHRvIGV4cGxvcmUgdHVtb3Igc2luZ2xlIGNlbGwgUk5BLVNlcSBkYXRhIHRvIGlkZW50aWZ5IGV2aWRlbmNlIGZvciBzb21hdGljIGxhcmdlLXNjYWxlIGNocm9tb3NvbWFsIGNvcHkgbnVtYmVyIGFsdGVyYXRpb25zLCBzdWNoIGFzIGdhaW5zIG9yIGRlbGV0aW9ucyBvZiBlbnRpcmUgY2hyb21vc29tZXMgb3IgbGFyZ2Ugc2VnbWVudHMgb2YgY2hyb21vc29tZXMuIFRoaXMgaXMgZG9uZSBieSBleHBsb3JpbmcgZXhwcmVzc2lvbiBpbnRlbnNpdHkgb2YgZ2VuZXMgYWNyb3NzIHBvc2l0aW9ucyBvZiB0dW1vciBnZW5vbWUgaW4gY29tcGFyaXNvbiB0byBhIHNldCBvZiByZWZlcmVuY2UgJ25vcm1hbCcgY2VsbHMuIEEgaGVhdG1hcCBpcyBnZW5lcmF0ZWQgaWxsdXN0cmF0aW5nIHRoZSByZWxhdGl2ZSBleHByZXNzaW9uIGludGVuc2l0aWVzIGFjcm9zcyBlYWNoIGNocm9tb3NvbWUsIGFuZCBpdCBvZnRlbiBiZWNvbWVzIHJlYWRpbHkgYXBwYXJlbnQgYXMgdG8gd2hpY2ggcmVnaW9ucyBvZiB0aGUgdHVtb3IgZ2Vub21lIGFyZSBvdmVyLWFidW5kYW50IG9yIGxlc3MtYWJ1bmRhbnQgYXMgY29tcGFyZWQgdG8gdGhhdCBvZiBub3JtYWwgY2VsbHMuDQoNClBlciBwYXRpZW50IGEgY252IGFuYWx5c2lzIHdpdGggYSBwcm92aWRlZCByZWZlcmVuY2UgZ3JvdXAuIFRoZSBnZW5lIG9yZGVyIGlzIG1hZGUgZnJvbSB0aGUgcHJvamVjdHMgLnBrYyBmaWxlLg0KDQpTZWxlY3QgdGhlIGFubm90YXRpb24gd2hlcmUgdGhlIENOViBhbmFseXNpcyBzaG91bGQgbG9vayBhdCBzcGVjaWZpY2FsbHkgYXMgZ3JvdXAgYW5kIHNwZWNpZnkgYSBzdWJncm91cCBhcyBncm91cF9maWx0ZXIgaWYgdGhlIGdyb3VwIG9mIGludGVyZXN0IGlzIGEgc3ViZ3JvdXAgaW5zaWRlIHRoZSBhbm5vdGF0aW9uLiBGb3IgZXhhbXBsZSBpZiB0aGUgQ05WIGFuYWx5c2lzIG5lZWRzIHRvIGJlIG9ubHkgb24gdHVtb3IgcmVnaW9ucyB0aGUgZ3JvdXAgd291bGQgYmUgQU5OWChBTk4gY29sdW1uIHdpdGggdGhlIGluZm8gYWJvdXQgdGhlIHR1bW9yIHJlZ2lvbnMpIGFuZCBncm91cF9maWx0ZXIgUGFuQ0srLiANClRoZSBhbm5vdGF0aW9uIHRoYXQgcmVmZXJlbmNlcyB0aGUgcGF0aWVudHMgYXMgcGF0aWVudHMuIElmIHRoZXJlIGFyZSBubyBwYXRpZW50cyBsZWF2ZSB0aGUgcGFyYW1ldGVyIGVtcHR5IGFzICIiLg0KVGhlIGFubm90YXRpb24gdGhhdCBzaG91bGQgYmUgaW5jbHVkZWQgaW4gdGhlIHJlc3VsdHMgaW5jbHVkaW5nIGEgcmVmZXJlbmNlIHNldCBhcyBjbnZfdGFyZ2V0Lg0KVGhpcyB3aWxsIGNyZWF0ZSBwYXRpZW50IHNwZWNpZmljIGZpbGVzIHdpdGggYWxsIHRoZSBpbmZvcm1hdGlvbiBpbmZlckNOViBuZWVkcy4NCg0KYGBge3IgY3JlYXRlIGNudiBmaWxlcywgaW5jbHVkZT1GQUxTRX0NCiMjIyMjIyMjI1BBUkFNRVRFUlMjIyMjIyMjIw0KZ3JvdXAgPC0gIiINCmdyb3VwX2ZpbHRlciA8LSAiIg0KDQpwYXRpZW50cyA8LSAiIg0KDQpjbnZfdGFyZ2V0IDwtICJBTk4xIg0KcHJpbnQocGFzdGUoIlNob3cgQ05WIG9uIiwgY252X3RhcmdldCwgIndpdGggdGhlIHNwZWNpZmljaXR5IG9uIHRoZSBncm91cHMgKGlmIGFueSk6IiwgZ3JvdXAsICI7IiwgZ3JvdXBfZmlsdGVyKSkNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQppZiAoIXBhdGllbnRzID09ICIiKSB7DQogIHBhdGllbnRfbGlzdCA8LSB1bmlxdWUodGFyZ2V0X0RhdGFbW3BhdGllbnRzXV0pDQp9IGVsc2V7DQogIHBhdGllbnRfbGlzdCA8LSAiZHVtbXkiDQp9DQoNCmZpbGUgPC0gcmVhZExpbmVzKFBLQ0ZpbGVzKQ0KZmlsZSA8LSBnc3ViKCcgJywgJycsDQogICAgICAgIGdzdWIoJyInLCAnJywgZmlsZSkpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEZvcm1hdCBhbmQgcmVtb3ZlIHNwYWNlDQoNCmRpc3BsYXkgPC0gZmlsZVtncmVwbCgiRGlzcGxheU5hbWUiLCBmaWxlLCBmaXhlZCA9IFRSVUUpXSAgICAgICAgICMgR3JhYiBkaXNwbGF5IG5hbWVzDQpkaXNwbGF5IDwtIGdzdWIoJ0Rpc3BsYXlOYW1lJywgIiIsDQogICAgICAgICAgIGdzdWIoJyInLCAiIiwNCiAgICAgICAgICAgZ3N1YignOicsICIiLA0KICAgICAgICAgICBnc3ViKCcsJywgIiIsDQogICAgICAgICAgIGdzdWIoJyAnLCAiIiwNCiAgICAgICAgICAgZ3N1YignXzAxJywgIiIsIGRpc3BsYXkpKSkpKSkgICAgICAgICAgICAgICAgICAgICAgICAgICMgR3JhYiBvbmx5IHRoZSBuYW1lcw0KZGlzcGxheSA8LSB1bmlxdWUoZGlzcGxheSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZW1vdmUgZHVwbGljYXRlcw0KDQpjaHIgPC0gZmlsZVtncmVwKCJHZW5vbWVDb29yZGluYXRlcyIsIGZpbGUpKzFdICAgICAgICAgICAgICAgICAgICAjIEdldCB0aGUgY2hyIHBvc2l0aW9ucyB1bmRlciB0aGUgR2Vub21lQ29vcmRpbmF0ZXMgbGluZQ0KY2hyIDwtZ3N1YignLCcsICIiLCBjaHIpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZW1vdmUgdW53YW50ZWQgc3ltYm9scw0KDQpwb3NpdGlvbnMgPC0gZGF0YS5mcmFtZSgNCiAgbmFtZSA9IGRpc3BsYXksDQogIGNociA9IGNocikNCnBvc2l0aW9ucyA8LSBwb3NpdGlvbnNbIWdyZXBsKCJUYXJnZXRTZXF1ZW5jZSIsIHBvc2l0aW9ucyRjaHIpLF0gICMgUmVtb3ZlIGVudHJpZXMgd2l0aG91dCBjb29yZGluYXRlcw0KDQpwb3NpdGlvbnMkY2hyIDwtIGdzdWIoJzonLCAnLScsIHBvc2l0aW9ucyRjaHIpICAgICAgICAgICAgICAgICAgICAjIEZvcm1hdCBmb3Igc3BsaXR0aW5nDQpwb3NpdGlvbnMkY2hyIDwtIHN0cl9zcGxpdF9maXhlZChwb3NpdGlvbnMkY2hyLCAiLSIsIDMpICAgICAgICAgICAjIFNwbGl0IGludG8gY2hyLCBiZWdpbiwgYW5kIGVuZCBwb3NpdGlvbg0KcG9zaXRpb25zIDwtIGFzLm1hdHJpeChwb3NpdGlvbnMpDQpwb3NpdGlvbnMgPC0gYXMuZGF0YS5mcmFtZShwb3NpdGlvbnMpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFN3YXAgdHlwZXMgdG8gcmVjb2duaXplIHNwbGl0IGNvbHVtbnMgYXMgc2luZ3VsYXIgY29sdW1ucw0KDQpwb3NpdGlvbnMkb3JkZXIgPC0gYXMubnVtZXJpYyhnc3ViKCdjaHInLCAnJywgcG9zaXRpb25zJGNoci4xKSkNCnBvc2l0aW9ucyA8LSBwb3NpdGlvbnNbb3JkZXIocG9zaXRpb25zJG9yZGVyLCBkZWNyZWFzaW5nID0gRkFMU0UpLCBdICMgT3JkZXIgdGhlIGNocm9tb3NvbWVzLg0KcG9zaXRpb25zJG9yZGVyIDwtIE5VTEwNCg0KY29sbmFtZXMocG9zaXRpb25zKSA8LSBOVUxMDQpyb3duYW1lcyhwb3NpdGlvbnMpIDwtIE5VTEwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluZmVyQ05WIHJlcXVpcmVzIG5vIGNvbHVtbiBhbmQgcm93IG5hbWVzDQoNCndyaXRlLnRhYmxlKHBvc2l0aW9ucywgImdlbmVfb3JkZXJfSHNfV1RBX3YxX3BrYy50eHQiLA0KICAgICAgICAgICAgc2VwID0gIlx0IiwNCiAgICAgICAgICAgIHF1b3RlID0gRkFMU0UsDQogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwNCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgV3JpdGUgYXdheQ0KDQoNCmZvciAocGF0aWVudCBpbiBwYXRpZW50X2xpc3QpIHsNCiAgaWYgKGdyb3VwX2ZpbHRlciAhPSAiIikgew0KICAgIGdyb3VwX2ludGVyZXN0IDwtIHRhcmdldF9EYXRhQHByb3RvY29sRGF0YUBkYXRhW1siU2FtcGxlSUQiXV1bdGFyZ2V0X0RhdGFbW2dyb3VwXV0gPT0gZ3JvdXBfZmlsdGVyICYgdGFyZ2V0X0RhdGFbW3BhdGllbnRzXV0gPT0gcGF0aWVudF0NCiAgfSBlbHNlIGlmIChsZW5ndGgocGF0aWVudF9saXN0KSA+IDEpIHsNCiAgICBncm91cF9pbnRlcmVzdCA8LSB0YXJnZXRfRGF0YUBwcm90b2NvbERhdGFAZGF0YVtbIlNhbXBsZUlEIl1dW3RhcmdldF9EYXRhW1twYXRpZW50c11dID09IHBhdGllbnRdDQogIH0gZWxzZSB7DQogICAgZ3JvdXBfaW50ZXJlc3QgPC0gdGFyZ2V0X0RhdGFAcHJvdG9jb2xEYXRhQGRhdGFbWyJTYW1wbGVJRCJdXQ0KICB9DQogIA0KICBjb3VudHMgPC0gdGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1siZXhwcnMiXV0NCiAgY29sbmFtZXMoY291bnRzKSA8LSBnc3ViKCcuZGNjJywgJycsY29sbmFtZXMoY291bnRzKSkNCiAgY291bnRzIDwtIGNvdW50c1ssY29sbmFtZXMoY291bnRzKSAlaW4lIGMoZ3JvdXBfaW50ZXJlc3QpXQ0KICB3cml0ZS50YWJsZShjb3VudHMsIHBhc3RlMChwYXRpZW50LCAiLkNvdW50cy50c3YiKSwgc2VwID0gIlx0IikgICAgICAgICAgICAgICAgICAgICMgTWFrZSByYXcgY291bnQgZmlsZQ0KDQogIGFubm90YXRpb24gPC0gdGFyZ2V0X0RhdGFAcGhlbm9EYXRhQGRhdGENCiAgYW5ub3RhdGlvbiRTYW1wbGVJRCA8LSBnc3ViKCcuZGNjJywnJywgcm93bmFtZXMoYW5ub3RhdGlvbikpDQogIGFubm90YXRpb24gPC0gYW5ub3RhdGlvblthbm5vdGF0aW9uJFNhbXBsZUlEICVpbiUgYyhncm91cF9pbnRlcmVzdCksXQ0KICAjYW5ub3RhdGlvbiA8LSBhbm5vdGF0aW9uW2MoIlNhbXBsZUlEIiwgInNlZ21lbnQiKV0gICAgICAgICAgICAgICAgICAjIFNlbGVjdCBTYW1wbGVJRCBhbmQgdGhlIG5lZWRlZCBhbm5vdGF0aW9uIChDTlYgcmVxdWlyZXMgU2FtcGxlSUQgYW5kIG9ubHkgMSBhbm5vdGF0aW9uKQ0KICBhbm5vdGF0aW9uIDwtIGFubm90YXRpb25bYygiU2FtcGxlSUQiLCBjbnZfdGFyZ2V0KV0NCiAgcm93bmFtZXMoYW5ub3RhdGlvbikgPC0gTlVMTA0KICBjb2xuYW1lcyhhbm5vdGF0aW9uKSA8LSBOVUxMICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW5mZXJDTlYgcmVxdWlyZXMgbm8gY29sdW1uIGFuZCByb3cgbmFtZXMNCiAgd3JpdGUudGFibGUoYW5ub3RhdGlvbiwgcGFzdGUwKHBhdGllbnQsICIuQW5ub3RhdGlvbnMudHN2IiksDQogICAgICAgICAgICAgIHNlcCA9ICJcdCIsDQogICAgICAgICAgICAgIHF1b3RlID0gRkFMU0UsDQogICAgICAgICAgICAgIGNvbC5uYW1lcyA9IEZBTFNFLA0KICAgICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWFrZSBhbm5vdGF0aW9uIGZpbGUNCn0NCmBgYA0KDQpTZWxlY3QgdGhlIHJlZmVyZW5jZSBzZXQgaW4gcmVmZXJlbmNlLiBUaGlzIGNhbiBiZSBtb3JlIHRoYW4gb25lLiBFdmVyeSBwYXRpZW50IHdpdGhvdXQgaXRzIG93biByZWZlcmVuY2Ugd2lsbCBub3QgYmUgYW5hbHlzZWQuDQoNCmBgYHtyIHJ1biBDTlYsIGVjaG89VCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQojIyMjIyMjIyNQQVJBTUVURVJTIyMjIyMjIyMNCnJlZmVyZW5jZSA8LSBjKCJub3JtYWwiKQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCmZhaWxlZCA8LSBjKCkNCmNudl9saXN0IDwtIGxpc3QoKQ0KDQpmb3IgKHBhdGllbnQgaW4gcGF0aWVudF9saXN0KSB7DQogICMgTmFtZSBvdXRwdXQgZm9sZGVyDQogIG91dF9kaXIgPC0gcGFzdGUwKHBhdGllbnQsICJfQ05WIikNCiAgaWYgKHN0cl9kZXRlY3QocGFzdGUocmVhZExpbmVzKHBhc3RlMChwYXRpZW50LCAiLkFubm90YXRpb25zLnRzdiIpKSwgY29sbGFwc2UgPSAnJyksIHJlZmVyZW5jZSkgPT0gRkFMU0UpIHsNCiAgICBmYWlsZWQgPC0gYyhmYWlsZWQsIHBhdGllbnQpDQogICAgbmV4dA0KICAgIH0NCg0KICAjIENyZWF0ZSB0aGUgaW5mZXJjbnYgb2JqZWN0DQogIGluZmVyY252X29iaiA9IENyZWF0ZUluZmVyY252T2JqZWN0KHJhd19jb3VudHNfbWF0cml4PSBwYXN0ZTAocGF0aWVudCwgIi5Db3VudHMudHN2IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25zX2ZpbGU9IHBhc3RlMChwYXRpZW50LCAiLkFubm90YXRpb25zLnRzdiIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWxpbT0iXHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX29yZGVyX2ZpbGU9ICJnZW5lX29yZGVyX0hzX1dUQV92MV9wa2MudHh0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI2dlbmVfb3JkZXJfZmlsZT0gImdlbmNvZGVfdjE5X2dlbmVfcG9zLnR4dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZl9ncm91cF9uYW1lcz0gcmVmZXJlbmNlLCAjIGlucHV0IHRoZSBub3JtYWwvcmVmZXJlbmNlIGdyb3VwIG5hbWVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNocl9leGNsdWRlID0gYygiY2hyTSIpKSNjKCJjaHJNIikpICMgRGVmYXVsdCBleGNsdWRlcyBjaHJYLCBjaHJZIGFuZCBjaHJNLiBCeSBvbmx5IHBpY2tpbmcgY2hyTSB5b3UgaW5jbHVkZSB0aGUgWCBhbmQgWSBjaHJvbW9zb21lcy4NCg0KICAjIHBlcmZvcm0gaW5mZXJjbnYgb3BlcmF0aW9ucyB0byByZXZlYWwgY252IHNpZ25hbC4gRm9yIGFsbCBvcHRpb25zOiBodHRwczovL3JkcnIuaW8vZ2l0aHViL2Jyb2FkaW5zdGl0dXRlL2luZmVyY252L21hbi9ydW4uaHRtbA0KICAjY252X2xpc3RbW3BhdGllbnRdXQ0KICBpbmZlcmNudl9vYmogPC0gaW5mZXJjbnY6OnJ1bihpbmZlcmNudl9vYmosDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3V0b2ZmPTAuMSwgICMgdXNlIDEgZm9yIHNtYXJ0LXNlcSwgMC4xIGZvciAxMHgtZ2Vub21pY3MNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvdXRfZGlyPSBvdXRfZGlyLCAgIyBkaXIgaXMgYXV0by1jcmVhdGVkIGZvciBzdG9yaW5nIG91dHB1dHMNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2J5X2dyb3Vwcz1GQUxTRSwgICAjIGNsdXN0ZXINCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZW5vaXNlPVRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSE1NPUZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR1bW9yX3N1YmNsdXN0ZXJfcGFydGl0aW9uX21ldGhvZCA9IGMoInFub3JtIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5hbHlzaXNfbW9kZSA9ICJzdWJjbHVzdGVycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9fcGxvdD1UUlVFDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyxkZWJ1Zz1UUlVFDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KDQogIHBsb3RfY252KGluZmVyY252X29iaiwNCiAgICAgICAgICBvdXRfZGlyID0gcGFzdGUwKHBhdGllbnQsICJfQ05WIiksDQogICAgICAgICAgdGl0bGUgPSAiaW5mZXJDTlYiLA0KICAgICAgICAgIG9ic190aXRsZSA9ICJPYnNlcnZhdGlvbnMgKENlbGxzKSIsDQogICAgICAgICAgcmVmX3RpdGxlID0gIlJlZmVyZW5jZXMgKENlbGxzKSIsDQogICAgICAgICAgY2x1c3Rlcl9ieV9ncm91cHMgPSBGQUxTRSwNCiAgICAgICAgICBjbHVzdGVyX3JlZmVyZW5jZXMgPSBGQUxTRSwNCiAgICAgICAgICBwbG90X2Nocl9zY2FsZSA9IEZBTFNFLA0KICAgICAgICAgICNjaHJfbGVuZ3RocyA9IE5VTEwsDQogICAgICAgICAga19vYnNfZ3JvdXBzID0gMSwNCiAgICAgICAgICBjb250aWdfY2V4ID0gMS41LA0KICAgICAgICAgICN4LmNlbnRlciA9IG1lYW4oaW5mZXJjbnZfb2JqQGV4cHIuZGF0YSksDQogICAgICAgICAgeC5yYW5nZSA9ICJhdXRvIiwNCiAgICAgICAgICAjaGNsdXN0X21ldGhvZCA9ICJ3YXJkLkQiLA0KICAgICAgICAgIG91dHB1dF9maWxlbmFtZSA9ICJpbmZlcmNudiIsDQogICAgICAgICAgb3V0cHV0X2Zvcm1hdCA9ICJwbmciLA0KICAgICAgICAgIHBuZ19yZXMgPSAzMDANCiAgICAgICAgICApDQp9DQpgYGANCg0KYGBge3Igc2hvdyBDTlYsIGZpZy5oZWlnaHQ9MzAsIGZpZy53aWR0aD0yNX0NCmZvciAocGF0aWVudCBpbiBwYXRpZW50X2xpc3QpIHsNCiAgcHJpbnQocGFzdGUoZ3JvdXBfZmlsdGVyLCBwYXRpZW50KSkNCiAgaWYgKHBhdGllbnQgJWluJSBmYWlsZWQpIHsNCiAgICBwcmludCgiIF4gICAgUGF0aWVudCBjb250YWluZWQgbm8gcmVmZXJlbmNlIGdyb3VwIikNCiAgICBuZXh0DQogIH0NCiAgaW1nIDwtIHJlYWRQTkcocGFzdGUocGFzdGUwKHBhdGllbnQsICJfQ05WIiksICIvaW5mZXJjbnYucG5nIiwgc2VwID0gIiIpKQ0KICBncmlkOjpncmlkLm5ld3BhZ2UoKQ0KICBncmlkOjpncmlkLnJhc3RlcihpbWcpDQp9DQpgYGANCg0KIyAxMS4xIERlbmRyb2dyYW0NCg0KQ2xvc2VyIGxvb2sgYXQgdGhlIGRlbmRyb2dyYW0gZnJvbSB0aGUgQ05WIGFuYWx5c2lzIHBlciBwYXRpZW50IHdoZXJlIHRoZSBub2RlcyBhcyB3cml0dGVuIG51bWJlcnMuIFVzZSB0aGUgbnVtYmVycyB0byBzZWxlY3Qgc3ViZ3JvdXBzIGZvciBmdXJ0aGVyIGFuYWx5c2lzIGluICd0LXRlc3Qgb24gdHdvIHN1Ymdyb3VwcycuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0yMH0NCiNvdXRfZGlyIDwtICJUMV9OQU5PXzAxMl9DTlYiDQp0cmVlcyA8LSBsaXN0KCkNCg0KZm9yIChwYXRpZW50IGluIHBhdGllbnRfbGlzdCkgew0KICBpZiAocGF0aWVudCAlaW4lIGZhaWxlZCkgew0KICAgICNwcmludCgiUGF0aWVudCBjb250YWluZWQgbm8gcmVmZXJlbmNlIGdyb3VwIikNCiAgICBuZXh0DQogIH0NCnRyZWUgPC0gcmVhZC50cmVlKHBhc3RlKHBhdGllbnQsIl9DTlYvaW5mZXJjbnYub2JzZXJ2YXRpb25zX2RlbmRyb2dyYW0udHh0Iiwgc2VwID0gIiIpKQ0Kb2J2IDwtIHJlYWQuY3N2KHBhc3RlKHBhdGllbnQsIl9DTlYvaW5mZXJjbnYub2JzZXJ2YXRpb25fZ3JvdXBpbmdzLnR4dCIsIHNlcCA9ICIiKSwgc2VwPSIiKQ0KdHJlZXNbW3BhdGllbnRdXSA8LSBnZ3RyZWUoDQogIHRyZWUsIGxhZGRlcml6ZT1GKSArDQogIGdlb21fdHJlZXNjYWxlKCkgKw0KICBnZW9tX3RpcGxhYihjb2xvcj1vYnYkQW5ub3RhdGlvbi5Db2xvciwgaGp1c3Q9LS4yKSArDQogIGNvb3JkX2NhcnRlc2lhbihjbGlwID0gJ29mZicpICsNCiAgdGhlbWVfdHJlZTIocGxvdC5tYXJnaW49bWFyZ2luKDYsIDIwMCwgNiwgNikpICsNCiAgZ2VvbV90ZXh0MihhZXMobGFiZWw9bm9kZSksIGhqdXN0PS0uMywgc2l6ZSA9IDMpICsNCiAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IHBhdGllbnQpDQp9DQoNCmdyaWQuYXJyYW5nZShncm9icz10cmVlcyxuY29sPTMpDQpgYGANCg0KIyMgdC10ZXN0IGNociB3aXRoIGdyb3Vwcw0KDQpDb21wYXJlIHRoZSBjb250cmFzdCB3aXRoaW4gYSBjaHJvbW9zb21lLiBTZWxlY3QgYSBjaHJvbW9zb21lLCBjb250cmFzdCwgYW5kIHBhdGllbnQgb2YgaW50ZXJlc3QgdG8gcnVuIGEgdC10ZXN0IG9uLg0KDQpgYGB7cn0NCiMjIyMjIyMjI1BBUkFNRVRFUlMjIyMjIyMjIw0KY2hyIDwtICJjaHJYIiAjIFNlbGVjdCBjaHJvbW9zb21lIG9mIGludGVyZXN0DQpjb250cmFzdCA8LSBjKCJub3JtYWwiLCAiREtEIikgIyBTZWxlY3QgY29udHJhc3QNCnBhdGllbnQgPC0gImR1bW15Ig0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCnBvc2l0aW9ucyA8LSBhcy5kYXRhLmZyYW1lKHBvc2l0aW9ucykNCmNvbG5hbWVzKHBvc2l0aW9ucykgPC0gYygiZ2VuZSIsICJjaHIiLCAiYmVnaW4iLCAiZW5kIikNCnNlbGVjdF9nZW5lcyA8LSBwb3NpdGlvbnMkZ2VuZVtwb3NpdGlvbnMkY2hyID09IGNocl0gIyBHcmFiIGNociBzcGVjaWZpYyBnZW5lcw0KDQphbm5vdGF0aW9uIDwtIGFzLmRhdGEuZnJhbWUocmVhZC5kZWxpbShwYXN0ZTAocGF0aWVudCwgIi5Bbm5vdGF0aW9ucy50c3YiKSwgaGVhZGVyPUZBTFNFKSkNCmNvbG5hbWVzKGFubm90YXRpb24pIDwtIGMoIlNhbXBsZV9JRCIsICJBTk4iKQ0Kc2VsZWN0X3NhbXBsZXMgPC0gYW5ub3RhdGlvbg0KDQpmaWx0ZXJfY291bnRzIDwtIGFzLmRhdGEuZnJhbWUodGFyZ2V0X0RhdGFAYXNzYXlEYXRhW1sibG9nX3EiXV0pDQpjb2xuYW1lcyhmaWx0ZXJfY291bnRzKSA8LSBnc3ViKCcuZGNjJywnJywgY29sbmFtZXMoZmlsdGVyX2NvdW50cykpDQpmaWx0ZXJfY291bnRzIDwtIGZpbHRlcl9jb3VudHNbLHNlbGVjdF9zYW1wbGVzJFNhbXBsZV9JRF0gIyBmaWx0ZXIgb3V0IHNhbXBsZXMgdGhhdCBhcmUgbm90IHRoZSBpbnRlcmVzdGluZyByZWdpb24NCg0Kc2VsZWN0X2dlbmVzIDwtIHNlbGVjdF9nZW5lc1tzZWxlY3RfZ2VuZXMgJWluJSByb3duYW1lcyhmaWx0ZXJfY291bnRzKV0gIyBPbmx5IHVzZSBnZW5lcyB0aGF0IGFyZSBhY3R1YWxseSBpbiB0aGUgZGF0YSAocGtjIGdlbmUgZmlsZSBoYXMgYWxsIG9mIHRoZW0pDQpmaWx0ZXJfY291bnRzIDwtIGZpbHRlcl9jb3VudHNbc2VsZWN0X2dlbmVzLF0gIyBmaWx0ZXIgb3V0IG5vbiBjaHJvbW9zb21lIHNwZWNpZmljIGdlbmVzDQpgYGANCg0KYGBge3IgdHRlc3QgY2hyLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMCB9DQpwbG90czwtbGlzdCgpDQp0YWJsZXM8LWxpc3QoKQ0KbGFiZWxzPC1saXN0KCkNCnRlc3Q8LSJ0dGVzdCINCm10YzwtIkJIIg0KY291bnRlcj0xDQoNCmxvZ19xX2ZpbHRlciA8LWFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykNCg0KY29tcHNfZGY8LWRhdGEuZnJhbWUoY29tcD0nJyx2YWw9JycpDQoNCmZvciAoYWN0aXZlX2dyb3VwMSBpbiBjb250cmFzdCkgew0KICAgIGZvciAoYWN0aXZlX2dyb3VwMiBpbiBjb250cmFzdCkgew0KDQogICAgICAjc3VwcmVzcyByZWR1bmNhbnQgY29tcGFyZXMNCiAgICAgIGlmKGFjdGl2ZV9ncm91cDE9PWFjdGl2ZV9ncm91cDIpIHtuZXh0fQ0KICAgICAgY29tcDwtcGFzdGUoc29ydChjKGFjdGl2ZV9ncm91cDEsYWN0aXZlX2dyb3VwMikpLGNvbGxhcHNlID0gIl8iKQ0KICAgICAgI3ByaW50KGNvbXApDQogICAgICBpZihjb21wICVpbiUgY29tcHNfZGYkY29tcCkge25leHR9DQogICAgICB0ZW1wX2RmPC1kYXRhLmZyYW1lKGNvbXA9Y29tcCAsdmFsPTEpDQogICAgICBjb21wc19kZjwtcmJpbmQoY29tcHNfZGYsdGVtcF9kZikNCg0KICAgICAgbGFiZWxzW1tjb3VudGVyXV08LXBhc3RlKGFjdGl2ZV9ncm91cDEsIiB2cyAiLCBhY3RpdmVfZ3JvdXAyKQ0KICAgICAgZ3JvdXAxPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJEFOTj09YWN0aXZlX2dyb3VwMV1dDQogICAgICBncm91cDI8LWxvZ19xX2ZpbHRlclssbmFtZXMoYXMuZGF0YS5mcmFtZShmaWx0ZXJfY291bnRzKSlbc2VsZWN0X3NhbXBsZXMkQU5OPT1hY3RpdmVfZ3JvdXAyXV0NCg0KICAgICAgI3J1biB0X3Rlc3RzDQogICAgICByZXN1bHRzPC1hcy5kYXRhLmZyYW1lICggYXBwbHkobG9nX3FfZmlsdGVyLCAxLCBmdW5jdGlvbih4KSB0LnRlc3QoeFtjb2xuYW1lcyhncm91cDEpXSx4W2NvbG5hbWVzKGdyb3VwMildKSRwLnZhbHVlKSApDQogICAgICBjb2xuYW1lcyhyZXN1bHRzKTwtInJhd19wX3ZhbHVlIg0KDQogICAgICAjbXVsdGlwbGVfdGVzdGluZ19jb3JyZWN0aW9uDQogICAgICBhZGpfcF92YWx1ZTwtIHAuYWRqdXN0KHJlc3VsdHMkcmF3X3BfdmFsdWUsbWV0aG9kPW10YykNCiAgICAgIHJlc3VsdHM8LWNiaW5kKHJlc3VsdHMsYWRqX3BfdmFsdWUpDQoNCiAgICAgICNjYWxjX2Zkcg0KICAgICAgRkRSPC0gcC5hZGp1c3QocmVzdWx0cyRyYXdfcF92YWx1ZSxtZXRob2Q9ImZkciIpDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLEZEUikNCg0KICAgICAgI2ZvbGRfY2hhbmdlcw0KICAgICAgI2FzIGJhc2UgZGF0YSBpcyBhbHJlYWR5IGxvZyB0cmFuc2Zvcm1lZCwgbWVhbnMgbmVlZCB0byBiZSBzdWJ0cmFjdGVkIHRvIGdldCBGQyBpbiBsb2cgc3BhY2UNCiAgICAgIGZjaGFuZ2VzPC1hcy5kYXRhLmZyYW1lKCBhcHBseShsb2dfcV9maWx0ZXIsIDEsIGZ1bmN0aW9uKHgpIChtZWFuKHhbY29sbmFtZXMoZ3JvdXAxKV0pIC0gbWVhbih4W2NvbG5hbWVzKGdyb3VwMildKSApICkgKQ0KICAgICAgY29sbmFtZXMoZmNoYW5nZXMpPC0iRkMiDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLGZjaGFuZ2VzKQ0KDQogICAgICAjYWRkIGdlbmVuYW1lcw0KICAgICAgcmVzdWx0cyRHZW5lPC1yb3duYW1lcyhyZXN1bHRzKQ0KDQogICAgICAjc2V0IGNhdGVnb3JpZXMgYmFzZWQgb24gUC12YWx1ZSAmIEZEUiBmb3IgcGxvdHRpbmcNCiAgICAgIHJlc3VsdHMkQ29sb3IgPC0gIk5TIG9yIEZDIDwgMC41Ig0KICAgICAgcmVzdWx0cyRDb2xvcltyZXN1bHRzJGFkal9wX3ZhbHVlIDwgMC4wNV0gPC0gIlAgPCAwLjA1Ig0KICAgICAgcmVzdWx0cyRDb2xvcltyZXN1bHRzJEZEUiA8IDAuMDVdIDwtICJGRFIgPCAwLjA1Ig0KICAgICAgcmVzdWx0cyRDb2xvcltyZXN1bHRzJEZEUiA8IDAuMDAxXSA8LSAiRkRSIDwgMC4wMDEiDQogICAgICByZXN1bHRzJENvbG9yW2FicyhyZXN1bHRzJEZDKSA8IDAuNV0gPC0gIk5TIG9yIEZDIDwgMC41Ig0KICAgICAgcmVzdWx0cyRDb2xvciA8LSBmYWN0b3IocmVzdWx0cyRDb2xvciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIk5TIG9yIEZDIDwgMC41IiwgIlAgPCAwLjA1IiwgIkZEUiA8IDAuMDUiLCAiRkRSIDwgMC4wMDEiKSkNCg0KICAgICAgI3Z1bGNhbm9wbG90DQoNCiAgICAgICMgcGljayB0b3AgZ2VuZXMgZm9yIGVpdGhlciBzaWRlIG9mIHZvbGNhbm8gdG8gbGFiZWwNCiAgICAgICMgb3JkZXIgZ2VuZXMgZm9yIGNvbnZlbmllbmNlOg0KDQogICAgICByZXN1bHRzJGludmVydF9QIDwtICgtbG9nMTAocmVzdWx0cyRhZGpfcF92YWx1ZSkpICogc2lnbihyZXN1bHRzJEZDKQ0KICAgICAgdG9wX2cgPC0gYygpDQogICAgICB0b3BfZyA8LSBjKHRvcF9nLA0KICAgICAgICAgICAgICAgICByZXN1bHRzW2luZCwgJ0dlbmUnXVsNCiAgICAgICAgICAgICAgICAgICBvcmRlcihyZXN1bHRzW2luZCwgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjE1XV0sDQogICAgICAgICAgICAgICAgIHJlc3VsdHNbaW5kLCAnR2VuZSddW29yZGVyKHJlc3VsdHNbaW5kLCAnaW52ZXJ0X1AnXSwgZGVjcmVhc2luZyA9IEZBTFNFKVsxOjE1XV0pDQogICAgICB0b3BfZzwtIHVuaXF1ZSh0b3BfZykNCiAgICAgIHJlc3VsdHMgPC0gcmVzdWx0c1ssIC0xKm5jb2wocmVzdWx0cyldICMgcmVtb3ZlIGludmVydF9QIGZyb20gbWF0cml4DQoNCiAgICAgICMgR3JhcGggcmVzdWx0cw0KICAgICAgcGxvdHNbW2NvdW50ZXJdXTwtIGdncGxvdChyZXN1bHRzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IEZDLCB5ID0gLWxvZzEwKGFkal9wX3ZhbHVlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gQ29sb3IsIGxhYmVsID0gR2VuZSkpICsNCiAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygwLjUsIC0wLjUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoMC4wNSksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICAgIGdlb21fcG9pbnQoKSArDQogICAgICAgIGxhYnMoeCA9IHBhc3RlKCJFbnJpY2hlZCBnZW5lcyBpbiIsIGNociwgIi0iLCBhY3RpdmVfZ3JvdXAyLCIgPC0gbG9nMihGQykgLT4gRW5yaWNoZWQgaW4iLCBhY3RpdmVfZ3JvdXAxKSwNCiAgICAgICAgICAgICB5ID0gIlNpZ25pZmljYW5jZSwgLWxvZzEwKFApIiwNCiAgICAgICAgICAgICBjb2xvciA9ICJTaWduaWZpY2FuY2UiKSArDQogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGBGRFIgPCAwLjAwMWAgPSAiZG9kZ2VyYmx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBGRFIgPCAwLjA1YCA9ICJsaWdodGJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgUCA8IDAuMDVgID0gIm9yYW5nZTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgTlMgb3IgRkMgPCAwLjVgID0gImdyYXkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDQpKSkgKw0KICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsMC4wNSkpKSArDQogICAgICAgIGdlb21fdGV4dF9yZXBlbChkYXRhID0gc3Vic2V0KHJlc3VsdHMsIEZEUjwwLjA1ICYgKC0wLjU+RkN8IEZDPjAuNSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMTUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZT0zLjUsDQogICAgICAgICAgICAgICAgICAgICAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAuMSwgYm94LnBhZGRpbmcgPSAuMiwgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1heC5vdmVybGFwcyA9IDUwKSArDQogICAgICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDIwKSArDQogICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogICAgICAgICNnZ3RpdGxlKHBhc3RlKHNsaWRlLCI6ICIsIHRlc3QsIG10YywibXVsdGl0ZXN0IGNvcnIiKSkNCiAgICAgICAgZ2d0aXRsZShwYXN0ZShwYXRpZW50LCAiOiAiLCB0ZXN0LCBtdGMsIm11bHRpdGVzdCBjb3JyIikpDQoNCiAgICAgICNzdG9yZSB0YWJsZXMgZm9yIGRpc3BsYXkgbGF0ZXINCiAgICAgIHRhYmxlc1tbY291bnRlcl1dPC1yZXN1bHRzDQoNCiAgICAgIGNvdW50ZXIgPSBjb3VudGVyKzENCiAgICAgICNkYXRhdGFibGUoc3Vic2V0KHJlc3VsdHMsIEdlbmUgJWluJSBHT0kpLCByb3duYW1lcz1GQUxTRSxjYXB0aW9uID0gcGFzdGUoIkRFIHJlc3VsdHMgIiwgYWN0aXZlX2dyb3VwMSwiIHZzICIsIGFjdGl2ZV9ncm91cDIpKQ0KICAgIH0NCiAgfQ0KDQpncmlkLmFycmFuZ2UoZ3JvYnM9cGxvdHMsbmNvbD0yKQ0KYGBgDQoNCiMgdC10ZXN0IG9uIHR3byBzdWJncm91cHMNCg0KU2VsZWN0IG5vZGVzIGZyb20gdGhlIGRlbmRyb2dyYW0gYXMgc3ViZ3JvdXBzLCBpbiB0aGlzIGNhc2Ugbm9kZXMgMTggYW5kIDE1LiBEZWNpZGUgb24gdGhlIGludm9sdmVkIGNocm9tb3NvbWVzIGFuZCBhZGQgdGhlbSBpbnRvIHRoZSBjaHJfbGlzdC4gU3BlY2lmeSB0aGUgcGF0aWVudCBpbiBwYXRpZW50Lg0KDQpgYGB7cn0NCiMjIyMjIyMjI1BBUkFNRVRFUlMjIyMjIyMjIw0KY2hyX2xpc3QgPC0gYygiY2hyMjIiKSAjIENob29zZSBuZWVkZWQgY2hyb21vc29tZXMgb2YgdGhlIHRhcmdldCBhcmVhLiBUaGluayBvZiBpdCBhcyB0aGUgeC1heGlzDQpwYXRpZW50IDwtICJkdW1teSINCm5vZGVfaW50ZXJlc3QgPC0gMjMzICMgQ2hvb3NlIHN1Ymdyb3VwIDENCm5vZGVfaW50ZXJlc3QyIDwtIDIxNiAjIENob29zZSBzdWJncm91cCAyDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KdHJlZSA8LSByZWFkLnRyZWUocGFzdGUocGF0aWVudCwiX0NOVi9pbmZlcmNudi5vYnNlcnZhdGlvbnNfZGVuZHJvZ3JhbS50eHQiLCBzZXAgPSAiIikpDQp0cmVlX2luZm8gPC0gdHJlZSAlPiUgYXMudHJlZWRhdGEgJT4lIGFzX3RpYmJsZSAjIHRyYW5zZm9ybSB0cmVlIGludG8gYWNjZXNzaWJsZSBkYXRhDQoNCnNhbXBsZXNfaW50ZXJlc3QgPC0gb2Zmc3ByaW5nKHRyZWVfaW5mbywgbm9kZV9pbnRlcmVzdCkgIyBHZXQgbGFiZWxzIGF0dGFjaGVkIHRvIHRoZSBncm91cA0Kc2FtcGxlc19pbnRlcmVzdCA8LSBhcy5jaGFyYWN0ZXIobmEub21pdChzYW1wbGVzX2ludGVyZXN0JGxhYmVsKSkgIyBmb3JtYXR0aW5nDQpwYXN0ZSgiU3ViZ3JvdXAgMTogIiwgc2FtcGxlc19pbnRlcmVzdCkNCg0Kc2FtcGxlc19pbnRlcmVzdDIgPC0gb2Zmc3ByaW5nKHRyZWVfaW5mbywgbm9kZV9pbnRlcmVzdDIpDQpzYW1wbGVzX2ludGVyZXN0MiA8LSBhcy5jaGFyYWN0ZXIobmEub21pdChzYW1wbGVzX2ludGVyZXN0MiRsYWJlbCkpDQpwYXN0ZSgiU3ViZ3JvdXAgMjogIiwgc2FtcGxlc19pbnRlcmVzdDIpDQpgYGANCg0KYGBge3J9DQpwb3NpdGlvbnMgPC0gYXMuZGF0YS5mcmFtZShwb3NpdGlvbnMpDQpjb2xuYW1lcyhwb3NpdGlvbnMpIDwtIGMoImdlbmUiLCAiY2hyIiwgImJlZ2luIiwgImVuZCIpICMgRm9ybWF0dGluZw0KDQpzZWxlY3RfZ2VuZXMgPC0gcG9zaXRpb25zJGdlbmVbcG9zaXRpb25zJGNociAlaW4lIGNocl9saXN0XSAjIHwgcG9zaXRpb25zJGNociA9PSBjaHIyXSAjIEdyYWIgY2hyb21vc29tZSBzcGVjaWZpYyBnZW5lcw0KDQphbm5vdGF0aW9uIDwtIGFzLmRhdGEuZnJhbWUocmVhZC5kZWxpbShwYXN0ZTAocGF0aWVudCwgIi5Bbm5vdGF0aW9ucy50c3YiKSwgaGVhZGVyPUZBTFNFKSkNCmNvbG5hbWVzKGFubm90YXRpb24pIDwtIGMoIlNhbXBsZV9JRCIsICJBTk4iKQ0Kc2VsZWN0X3NhbXBsZXMgPC0gYW5ub3RhdGlvblthbm5vdGF0aW9uJFNhbXBsZV9JRCAlaW4lIHNhbXBsZXNfaW50ZXJlc3QgfCBhbm5vdGF0aW9uJFNhbXBsZV9JRCAlaW4lIHNhbXBsZXNfaW50ZXJlc3QyLF0gIyBHcmFiIHN1Ymdyb3VwIHNwZWNpZmljIFNhbXBsZSBJRHMNCg0KZmlsdGVyX2NvdW50cyA8LSB0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJsb2dfcSJdXQ0KY29sbmFtZXMoZmlsdGVyX2NvdW50cykgPC0gZ3N1YignLmRjYycsJycsIGNvbG5hbWVzKGZpbHRlcl9jb3VudHMpKQ0KZmlsdGVyX2NvdW50cyA8LSBmaWx0ZXJfY291bnRzWyxzZWxlY3Rfc2FtcGxlcyRTYW1wbGVfSURdICMgRmlsdGVyIFNhbXBsZSBJRCdzDQoNCnNlbGVjdF9nZW5lcyA8LSBzZWxlY3RfZ2VuZXNbc2VsZWN0X2dlbmVzICVpbiUgcm93bmFtZXMoZmlsdGVyX2NvdW50cyldICMgT25seSB1c2UgZ2VuZXMgdGhhdCBhcmUgYWN0dWFsbHkgaW4gdGhlIGRhdGEgKHBrYyBnZW5lIGZpbGUgaGFzIGFsbCBvZiB0aGVtKQ0KZmlsdGVyX2NvdW50cyA8LSBmaWx0ZXJfY291bnRzW3NlbGVjdF9nZW5lcyxdICMgRmlsdGVyIGdlbmVzDQpgYGANCg0KYGBge3IgdHRlc3Qgc3ViZ3JvdXBzLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMH0NCnBsb3RzPC1saXN0KCkNCnRhYmxlczwtbGlzdCgpDQpsYWJlbHM8LWxpc3QoKQ0KdGVzdDwtInR0ZXN0Ig0KbXRjPC0iQkgiDQpjb3VudGVyPTENCg0KbG9nX3FfZmlsdGVyIDwtYXMuZGF0YS5mcmFtZShmaWx0ZXJfY291bnRzKQ0KDQpjb21wc19kZjwtZGF0YS5mcmFtZShjb21wPScnLHZhbD0nJykNCg0KDQphY3RpdmVfZ3JvdXAxIDwtIHNhbXBsZXNfaW50ZXJlc3QgI3N1Ymdyb3VwIDENCmFjdGl2ZV9ncm91cDIgPC0gc2FtcGxlc19pbnRlcmVzdDIgI3N1Ymdyb3VwIDINCg0KIyBmb3IgKGFjdGl2ZV9ncm91cDEgaW4gYygic3ViMSIpKSB7DQojICAgICBmb3IgKGFjdGl2ZV9ncm91cDIgaW4gYygic3ViMiIpKSB7DQoNCiAgICAgICNzdXByZXNzIHJlZHVuY2FudCBjb21wYXJlcw0KICAgICAgI2lmKGFjdGl2ZV9ncm91cDE9PWFjdGl2ZV9ncm91cDIpIHtuZXh0fQ0KICAgICAgI2NvbXA8LXBhc3RlKHNvcnQoYyhhY3RpdmVfZ3JvdXAxLGFjdGl2ZV9ncm91cDIpKSxjb2xsYXBzZSA9ICJfIikNCiAgICAgICNwcmludChjb21wKQ0KICAgICAgI2lmKGNvbXAgJWluJSBjb21wc19kZiRjb21wKSB7bmV4dH0NCiAgICAgIHRlbXBfZGY8LWRhdGEuZnJhbWUoY29tcD1jb21wICx2YWw9MSkNCiAgICAgIGNvbXBzX2RmPC1yYmluZChjb21wc19kZix0ZW1wX2RmKQ0KDQogICAgICAjIGxhYmVsc1tbY291bnRlcl1dPC1wYXN0ZShhY3RpdmVfZ3JvdXAxLCIgdnMgIiwgYWN0aXZlX2dyb3VwMikNCiAgICAgICMgZ3JvdXAxPC1sb2dfcV9maWx0ZXJbLG5hbWVzKGFzLmRhdGEuZnJhbWUoZmlsdGVyX2NvdW50cykpW3NlbGVjdF9zYW1wbGVzJEFOTj09YWN0aXZlX2dyb3VwMV1dDQogICAgICAjIGdyb3VwMjwtbG9nX3FfZmlsdGVyWyxuYW1lcyhhcy5kYXRhLmZyYW1lKGZpbHRlcl9jb3VudHMpKVtzZWxlY3Rfc2FtcGxlcyRBTk49PWFjdGl2ZV9ncm91cDJdXQ0KDQogICAgICBsYWJlbHNbW2NvdW50ZXJdXTwtcGFzdGUoYWN0aXZlX2dyb3VwMSwiIHZzICIsIGFjdGl2ZV9ncm91cDIpDQogICAgICBncm91cDE8LWxvZ19xX2ZpbHRlclssbmFtZXMoYXMuZGF0YS5mcmFtZShmaWx0ZXJfY291bnRzKSlbc2VsZWN0X3NhbXBsZXMkU2FtcGxlX0lEICVpbiUgYWN0aXZlX2dyb3VwMV1dDQogICAgICBncm91cDI8LWxvZ19xX2ZpbHRlclssbmFtZXMoYXMuZGF0YS5mcmFtZShmaWx0ZXJfY291bnRzKSlbc2VsZWN0X3NhbXBsZXMkU2FtcGxlX0lEICVpbiUgYWN0aXZlX2dyb3VwMl1dDQoNCiAgICAgICNydW4gdF90ZXN0cw0KICAgICAgcmVzdWx0czwtYXMuZGF0YS5mcmFtZSAoIGFwcGx5KGxvZ19xX2ZpbHRlciwgMSwgZnVuY3Rpb24oeCkgdC50ZXN0KHhbY29sbmFtZXMoZ3JvdXAxKV0seFtjb2xuYW1lcyhncm91cDIpXSkkcC52YWx1ZSkgKQ0KICAgICAgY29sbmFtZXMocmVzdWx0cyk8LSJyYXdfcF92YWx1ZSINCg0KICAgICAgI211bHRpcGxlX3Rlc3RpbmdfY29ycmVjdGlvbg0KICAgICAgYWRqX3BfdmFsdWU8LSBwLmFkanVzdChyZXN1bHRzJHJhd19wX3ZhbHVlLG1ldGhvZD1tdGMpDQogICAgICByZXN1bHRzPC1jYmluZChyZXN1bHRzLGFkal9wX3ZhbHVlKQ0KDQogICAgICAjY2FsY19mZHINCiAgICAgIEZEUjwtIHAuYWRqdXN0KHJlc3VsdHMkcmF3X3BfdmFsdWUsbWV0aG9kPSJmZHIiKQ0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxGRFIpDQoNCiAgICAgICNmb2xkX2NoYW5nZXMNCiAgICAgICNhcyBiYXNlIGRhdGEgaXMgYWxyZWFkeSBsb2cgdHJhbnNmb3JtZWQsIG1lYW5zIG5lZWQgdG8gYmUgc3VidHJhY3RlZCB0byBnZXQgRkMgaW4gbG9nIHNwYWNlDQogICAgICBmY2hhbmdlczwtYXMuZGF0YS5mcmFtZSggYXBwbHkobG9nX3FfZmlsdGVyLCAxLCBmdW5jdGlvbih4KSAobWVhbih4W2NvbG5hbWVzKGdyb3VwMSldKSAtIG1lYW4oeFtjb2xuYW1lcyhncm91cDIpXSkgKSApICkNCiAgICAgIGNvbG5hbWVzKGZjaGFuZ2VzKTwtIkZDIg0KICAgICAgcmVzdWx0czwtY2JpbmQocmVzdWx0cyxmY2hhbmdlcykNCg0KICAgICAgI2FkZCBnZW5lbmFtZXMNCiAgICAgIHJlc3VsdHMkR2VuZTwtcm93bmFtZXMocmVzdWx0cykNCg0KICAgICAgI3NldCBjYXRlZ29yaWVzIGJhc2VkIG9uIFAtdmFsdWUgJiBGRFIgZm9yIHBsb3R0aW5nDQogICAgICByZXN1bHRzJENvbG9yIDwtICJOUyBvciBGQyA8IDAuNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRhZGpfcF92YWx1ZSA8IDAuMDVdIDwtICJQIDwgMC4wNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRGRFIgPCAwLjA1XSA8LSAiRkRSIDwgMC4wNSINCiAgICAgIHJlc3VsdHMkQ29sb3JbcmVzdWx0cyRGRFIgPCAwLjAwMV0gPC0gIkZEUiA8IDAuMDAxIg0KICAgICAgcmVzdWx0cyRDb2xvclthYnMocmVzdWx0cyRGQykgPCAwLjVdIDwtICJOUyBvciBGQyA8IDAuNSINCiAgICAgIHJlc3VsdHMkQ29sb3IgPC0gZmFjdG9yKHJlc3VsdHMkQ29sb3IsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOUyBvciBGQyA8IDAuNSIsICJQIDwgMC4wNSIsICJGRFIgPCAwLjA1IiwgIkZEUiA8IDAuMDAxIikpDQoNCiAgICAgICN2dWxjYW5vcGxvdA0KDQogICAgICAjIHBpY2sgdG9wIGdlbmVzIGZvciBlaXRoZXIgc2lkZSBvZiB2b2xjYW5vIHRvIGxhYmVsDQogICAgICAjIG9yZGVyIGdlbmVzIGZvciBjb252ZW5pZW5jZToNCg0KICAgICAgcmVzdWx0cyRpbnZlcnRfUCA8LSAoLWxvZzEwKHJlc3VsdHMkYWRqX3BfdmFsdWUpKSAqIHNpZ24ocmVzdWx0cyRGQykNCiAgICAgIHRvcF9nIDwtIGMoKQ0KICAgICAgdG9wX2cgPC0gYyh0b3BfZywNCiAgICAgICAgICAgICAgICAgcmVzdWx0c1tpbmQsICdHZW5lJ11bDQogICAgICAgICAgICAgICAgICAgb3JkZXIocmVzdWx0c1tpbmQsICdpbnZlcnRfUCddLCBkZWNyZWFzaW5nID0gVFJVRSlbMToxNV1dLA0KICAgICAgICAgICAgICAgICByZXN1bHRzW2luZCwgJ0dlbmUnXVtvcmRlcihyZXN1bHRzW2luZCwgJ2ludmVydF9QJ10sIGRlY3JlYXNpbmcgPSBGQUxTRSlbMToxNV1dKQ0KICAgICAgdG9wX2c8LSB1bmlxdWUodG9wX2cpDQogICAgICByZXN1bHRzIDwtIHJlc3VsdHNbLCAtMSpuY29sKHJlc3VsdHMpXSAjIHJlbW92ZSBpbnZlcnRfUCBmcm9tIG1hdHJpeA0KDQogICAgICAjIEdyYXBoIHJlc3VsdHMNCiAgICAgICNwbG90c1tbY291bnRlcl1dPC0gZ2dwbG90KHJlc3VsdHMsDQogICAgICBwIDwtIGdncGxvdChyZXN1bHRzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IEZDLCB5ID0gLWxvZzEwKGFkal9wX3ZhbHVlKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gQ29sb3IsIGxhYmVsID0gR2VuZSkpICsNCiAgICAgICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygwLjUsIC0wLjUpLCBsdHkgPSAiZGFzaGVkIikgKw0KICAgICAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoMC4wNSksIGx0eSA9ICJkYXNoZWQiKSArDQogICAgICAgIGdlb21fcG9pbnQoKSArDQogICAgICAgIGxhYnMoeCA9IHBhc3RlKCJFbnJpY2hlZCBnZW5lcyBmcm9tIiwgY2hyX2xpc3QsICJpbiIsICJzdWJncm91cCAyIiwiIDwtIGxvZzIoRkMpIC0+IEVucmljaGVkIGluIiwgInN1Ymdyb3VwIDEiKSwNCiAgICAgICAgICAgICB5ID0gIlNpZ25pZmljYW5jZSwgLWxvZzEwKFApIiwNCiAgICAgICAgICAgICBjb2xvciA9ICJTaWduaWZpY2FuY2UiKSArDQogICAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKGBGRFIgPCAwLjAwMWAgPSAiZG9kZ2VyYmx1ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGBGRFIgPCAwLjA1YCA9ICJsaWdodGJsdWUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgUCA8IDAuMDVgID0gIm9yYW5nZTIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgTlMgb3IgRkMgPCAwLjVgID0gImdyYXkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2l6ZSA9IDQpKSkgKw0KICAgICAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsMC4wNSkpKSArDQogICAgICAgIGdlb21fdGV4dF9yZXBlbChkYXRhID0gc3Vic2V0KHJlc3VsdHMsIEZEUjwwLjA1ICYgKC0wLjU+RkN8IEZDPjAuNSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgcG9pbnQucGFkZGluZyA9IDAuMTUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZT0zLjUsDQogICAgICAgICAgICAgICAgICAgICAgICBtaW4uc2VnbWVudC5sZW5ndGggPSAuMSwgYm94LnBhZGRpbmcgPSAuMiwgbHdkID0gMiwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1heC5vdmVybGFwcyA9IDUwKSArDQogICAgICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDIwKSArDQogICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArDQogICAgICAgICNnZ3RpdGxlKHBhc3RlKHNsaWRlLCI6ICIsIHRlc3QsIG10YywibXVsdGl0ZXN0IGNvcnIiKSkNCiAgICAgICAgZ2d0aXRsZShwYXN0ZShwYXRpZW50LCAiOiAiLCB0ZXN0LCBtdGMsIm11bHRpdGVzdCBjb3JyIikpDQoNCiAgICAgICNzdG9yZSB0YWJsZXMgZm9yIGRpc3BsYXkgbGF0ZXINCiAgICAgIHRhYmxlc1tbY291bnRlcl1dPC1yZXN1bHRzDQoNCiAgICAgIGNvdW50ZXIgPSBjb3VudGVyKzENCiAgICAgICNkYXRhdGFibGUoc3Vic2V0KHJlc3VsdHMsIEdlbmUgJWluJSBHT0kpLCByb3duYW1lcz1GQUxTRSxjYXB0aW9uID0gcGFzdGUoIkRFIHJlc3VsdHMgIiwgYWN0aXZlX2dyb3VwMSwiIHZzICIsIGFjdGl2ZV9ncm91cDIpKQ0KICAjICAgfQ0KICAjIH0NCg0KI2dncGxvdGx5KHApDQpwDQojZ3JpZC5hcnJhbmdlKGdyb2JzPXBsb3RzLG5jb2w9MikNCmBgYA0KDQojIDEyIENvZGUgJiBWZXJzaW9ucw0KDQpQaXBlbGluZXZlcnNpb246IHYxIGJhc2VkIG9uOg0KPGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC93b3JrZmxvd3MvdmlnbmV0dGVzL0dlb014V29ya2Zsb3dzL2luc3QvZG9jL0dlb214VG9vbHNfUk5BLU5HU19BbmFseXNpcy5odG1sPg0KDQpUaGUgdW5kZXJseWluZyBjb2RlIGNhbiBiZSBkb3dubG9hZGVkIGZyb20gdGhlICdDb2RlJywgYnV0dG9uIG9uIHRoZSB0b3ANCm9mIHRoaXMgcGFnZS4gQ2hvb3NlIG9wdGlvbiAnZG93bmxvYWQgUm1kJyB0byBkb3dubG9hZCB0aGUgZnVsbCBwaXBlbGluZQ0Kd2hpY2ggY2FuIGJlIG9wZW5lZCBpbiBSIG9yIFJzdHVkaW8uIFNvbWUgZmlsZXBhdGhzIGFyZSBoYXJkY29kZWQgYW5kDQpuZWVkIHRvIGJlIGNoYW5nZWQgYWNjb3JkaW5nIHRvIHlvdXIgc2V0dXAuDQoNCiMgMTIuMSBSIHNlc3Npb24gaW5mb3JtYXRpb24NCg0KYGBge3Igc2Vzc2lvbl9pbmZvfQ0Kc2Vzc2lvbkluZm8oKQ0KYGBgDQoNCiMjIDEyLjIgUmVmZXJlbmNlcw0KDQoxIExldmkgdmFuIEhpamZ0ZSwgTWFyam9sZWluIEdldXJ0cywgV2llcyBSLiBWYWxsZW50Z29lZCwgUGF1bCBILkMuIEVpbGVycywgUGV0ZXIgQS5FLiBTaWxsZXZpcyBTbWl0dCwgUmVubyBEZWJldHMsIFBpbSBKLiBGcmVuY2gsDQpBbHRlcm5hdGl2ZSBub3JtYWxpemF0aW9uIGFuZCBhbmFseXNpcyBwaXBlbGluZSB0byBhZGRyZXNzIHN5c3RlbWF0aWMgYmlhcyBpbiBOYW5vU3RyaW5nIEdlb014IERpZ2l0YWwgU3BhdGlhbCBQcm9maWxpbmcgZGF0YSwNCmlTY2llbmNlLCBWb2x1bWUgMjYsIElzc3VlIDEsIDIwMjMsIDEwNTc2MCwgSVNTTiAyNTg5LTAwNDIsIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE2L2ouaXNjaS4yMDIyLjEwNTc2MC4NCg0KMiBaaGFvLCBZLiwgV29uZywgTC4gJiBHb2gsIFcuVy5CLiBIb3cgdG8gZG8gcXVhbnRpbGUgbm9ybWFsaXphdGlvbiBjb3JyZWN0bHkgZm9yIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGFuYWx5c2VzLiBTY2kgUmVwIDEwLCAxNTUzNCAoMjAyMCkuIGh0dHBzOi8vZG9pLm9yZy8xMC4xMDM4L3M0MTU5OC0wMjAtNzI2NjQtNg0KDQohW10oaHR0cDovL3VzZXEubmwvd3AtY29udGVudC91cGxvYWRzLzIwMjIvMTIvZGVjb3JhdGlvbi1zdHJva2UtZmxhdC5wbmcpDQoNCmBgYHtyfQ0Ka25pdHI6OmtuaXRfZXhpdCgpDQpgYGANCg0KIyA5LjAgcmVsb2FkIGRhdGENCg0KYGBge3Igc3BhdGlhbF9kZWNvbl9wcmVwYXJlfQ0KI3JlbG9hZCBkYXRhDQojIERhdGEgPC0NCiMgICByZWFkTmFub1N0cmluZ0dlb014U2V0KGRjY0ZpbGVzID0gRENDRmlsZXMsDQojICAgICAgICAgICAgICAgICAgICAgICAgICBwa2NGaWxlcyA9IFBLQ0ZpbGVzLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm9EYXRhRmlsZSA9IFNhbXBsZUFubm90YXRpb25GaWxlLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgcGhlbm9EYXRhU2hlZXQgPSAiU2hlZXQxIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHBoZW5vRGF0YURjY0NvbE5hbWUgPSAiU2FtcGxlX0lEIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3RvY29sRGF0YUNvbE5hbWVzID0gYygiYW9pIiwgInJvaSIpLA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwZXJpbWVudERhdGFDb2xOYW1lcyA9IGMoInBhbmVsIikpDQojIA0KIyBwa2NzIDwtIGFubm90YXRpb24oRGF0YSkNCiMgbW9kdWxlcyA8LSBnc3ViKCIucGtjIiwgIiIsIHBrY3MpDQojIA0KIyAjc2hpZnQgYW55IGV4cHJlc3Npb24gY291bnRzIHdpdGggYSB2YWx1ZSBvZiAwIHRvIDEgdG8gZW5hYmxlIGluIGRvd25zdHJlYW0gdHJhbnNmb3JtYXRpb25zLg0KIyBEYXRhIDwtIHNoaWZ0Q291bnRzT25lKERhdGEsIHVzZURBTG9naWMgPSBUUlVFKQ0KIyANCiMgI2NvbGxhcHNfdGFyZ2V0cw0KIyB0YXJnZXRfRGF0YSA8LSBhZ2dyZWdhdGVDb3VudHMoRGF0YSkNCiMgZGltKHRhcmdldF9EYXRhKQ0KIyANCiMgI25vcm1hbGl6ZQ0KIyB0YXJnZXRfRGF0YSA8LSBub3JtYWxpemUodGFyZ2V0X0RhdGEgLCBkYXRhX3R5cGUgPSAiUk5BIiwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX21ldGhvZCA9ICJxdWFudCIsIA0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lyZWRRdWFudGlsZSA9IC43NSwNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b0VsdCA9ICJxX25vcm0iKQ0KYGBgDQoNCiMgOS40IFJ1biBhZHZhbmNlZCBzcGF0aWFsIGRldm9uY29sdXRpb24NCg0KYGBge3J9DQojIHZlY3RvciBpZGVudGlmeWluZyBwdXJlIHR1bW9yIHNlZ21lbnRzOg0KdGFyZ2V0X0RhdGEkaXN0dW1vciA9IHRhcmdldF9EYXRhJEFOTjIgPT0gIkVwaXRoZWxpdW0iDQoNCiMgcnVuIHNwYXRpYWxkZWNvbiB3aXRoIGFsbCB0aGUgYmVsbHMgYW5kIHdoaXN0bGVzOg0KcmVzdGlscyA9IHJ1bnNwYXRpYWxkZWNvbihvYmplY3QgPSB0YXJnZXRfRGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9lbHQgPSAicV9ub3JtIiwgICAgICAgICAgICAgICAgICAgICMgbm9ybWFsaXplZCBkYXRhDQogICAgICAgICAgICAgICAgICAgICAgICAgIHJhd19lbHQgPSAiZXhwcnMiLCAgICAgICAgICAgICAgICAgICAgICAjIGV4cGVjdGVkIGJhY2tncm91bmQgY291bnRzIGZvciBldmVyeSBkYXRhIHBvaW50IGluIG5vcm0NCiAgICAgICAgICAgICAgICAgICAgICAgICAgWCA9IHNhZmVUTUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgc2FmZVRNRSBtYXRyaXgsIHVzZWQgYnkgZGVmYXVsdA0KICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsbWVyZ2VzID0gc2FmZVRNRS5tYXRjaGVzLCAgICAgICAgICAgIyBzYWZlVE1FLm1hdGNoZXMgb2JqZWN0LCB1c2VkIGJ5IGRlZmF1bHQNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY2VsbF9jb3VudHMgPSB0YXJnZXRfRGF0YSRudWNsZWksICAgICAgIyBudWNsZWkgY291bnRzLCB1c2VkIHRvIGVzdGltYXRlIHRvdGFsIGNlbGxzDQogICAgICAgICAgICAgICAgICAgICAgICAgIGlzX3B1cmVfdHVtb3IgPSB0YXJnZXRfRGF0YSRpc3R1bW9yLCAgICMgaWRlbnRpdGllcyBvZiB0aGUgVHVtb3Igc2VnbWVudHMvb2JzZXJ2YXRpb25zDQogICAgICAgICAgICAgICAgICAgICAgICAgIG5fdHVtb3JfY2x1c3RlcnMgPSA1KSAgICAgICAgICAgICAgICAgICAjIGhvdyBtYW55IGRpc3RpbmN0IHR1bW9yIHByb2ZpbGVzIHRvIGFwcGVuZCB0byBzYWZlVE1FDQoNCiNzdHIocERhdGEocmVzdGlscykpDQpoZWF0bWFwKHN3ZWVwKHJlc3RpbHNAZXhwZXJpbWVudERhdGFAb3RoZXIkU3BhdGlhbERlY29uTWF0cml4LCAxLCBhcHBseShyZXN0aWxzQGV4cGVyaW1lbnREYXRhQG90aGVyJFNwYXRpYWxEZWNvbk1hdHJpeCwgMSwgbWF4KSwgIi8iKSwNCiAgICAgICAgIGxhYlJvdyA9IE5BLCBtYXJnaW5zID0gYygxMCwgNSkpDQoNCmBgYA0KDQo8IS0tICMgOS4zLjIgUGxvdHRpbmcgZGVjb252b2x1dGlvbiByZXN1bHRzIC0tPg0KDQo8IS0tIGBgYHtyLCBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD03fSAtLT4NCg0KPCEtLSAjIEZvciByZWZlcmVuY2UsIHNob3cgdGhlIFRJTHMgY29sb3IgZGF0YSBvYmplY3QgdXNlZCBieSB0aGUgcGxvdHRpbmcgZnVuY3Rpb25zICAtLT4NCg0KPCEtLSAjIHdoZW4gc2FmZVRNRSBoYXMgYmVlbiB1c2VkOiAtLT4NCg0KPCEtLSBkYXRhKCJjZWxsY29scyIpIC0tPg0KDQo8IS0tICNjZWxsY29scyAtLT4NCg0KPCEtLSBvID0gaGNsdXN0KGRpc3QodChyZXMkY2VsbC5jb3VudHMkY2VsbC5jb3VudHMpKSkkb3JkZXIgLS0+DQoNCjwhLS0gbGF5b3V0KG1hdHJpeChjKDEsIDIpLCAxKSwgd2lkdGhzID0gYyg3LCAzKSkgLS0+DQoNCjwhLS0gVElMX2JhcnBsb3QodChyZXMkY2VsbC5jb3VudHMkY2VsbC5jb3VudHNbLCBvXSksIC0tPg0KDQo8IS0tICAgICAgICAgICAgIGhvcml6ID0gVFJVRSwgZHJhd19sZWdlbmQgPSBUUlVFLCBjZXgubmFtZXMgPSAwLjkpIC0tPg0KDQo8IS0tICNwYXIobWFyPWMoMiwgMTUsIDIsIDIpKSAtLT4NCg0KPCEtLSAjIG9yIHRoZSBwcm9wb3J0aW9ucyBvZiBjZWxsczogLS0+DQoNCjwhLS0gdGVtcCA9IHJlcGxhY2UocmVzJHByb3Bfb2Zfbm9udHVtb3IsIGlzLm5hKHJlcyRwcm9wX29mX25vbnR1bW9yKSwgMCkgLS0+DQoNCjwhLS0gbyA9IGhjbHVzdChkaXN0KHRlbXBbcmVzJEFOTjIgPT0gIkNENDUiLF0pKSRvcmRlciAtLT4NCg0KPCEtLSBUSUxfYmFycGxvdCh0KHJlcyRwcm9wX29mX2FsbCksICAtLT4NCg0KPCEtLSAgICAgICAgICAgICBob3JpeiA9IFRSVUUsIGRyYXdfbGVnZW5kID0gVFJVRSwgY2V4Lm5hbWVzID0gMC45KSAtLT4NCg0KPCEtLSBgYGAgLS0+DQoNCiMgOS4zLjMgRmxvcmV0cyBvZiBTcGF0aWFsIGRlY29udm9sdXRpb24NCg0KVGhlIHNlY29uZCBmdW5jdGlvbiBpcyAiZmxvcmV0cyIsIHVzZWQgZm9yIHBsb3R0aW5nIGNlbGwgYWJ1bmRhbmNlcyBhdG9wDQpzb21lIDItRCBwcm9qZWN0aW9uLiBIZXJlLCB3ZSdsbCBwbG90IGNlbGwgYWJ1bmRhbmNlcyBhdG9wIHRoZSBmaXJzdCAyDQpwcmluY2lwYWwgY29tcG9uZW50cyBvZiB0aGUgZGF0YToNCg0KYGBge3J9DQp4eSA8LSByZWFkX2V4Y2VsKCJMOi9wa2xvb3N0ZXJtYW4vS2lkbmV5X29yZ2FuX2F0bGFzL2Fubm90YXRpb24vT3JnYW5BdGxhc19LaWRuZXkueGxzeCIsIHNoZWV0ID0gIlNlZ21lbnRQcm9wZXJ0aWVzIikNCnh5JG5ld3ggPC0gKHh5JFJPSUNvb3JkaW5hdGVYIC0geHkkU2Nhbk9mZnNldFgpDQp4eSRuZXd5IDwtICh4eSRST0lDb29yZGluYXRlWSAtIHh5JFNjYW5PZmZzZXRZKQ0KeHkgPC0geHlbLGMoIlNlZ21lbnREaXNwbGF5TmFtZSIsICJuZXd4IiwgIm5ld3kiKV0NCnh5WyJhb2kiXSA8LSB4eVsiU2VnbWVudERpc3BsYXlOYW1lIl0NCmFubm90IDwtIHRhcmdldF9EYXRhQHByb3RvY29sRGF0YUBkYXRhDQphbm5vdCA8LSBhbm5vdCAlPiUgaW5uZXJfam9pbih4eSwgYnkgPSAnYW9pJykgIyU+JSBzZWxlY3QoRGVzY3JpcHRpb24sIElELCBnZW5lSUQsIGxlYWRpbmdFZGdlLCBwYWRqKQ0KYW5ub3QkeCA8LSBhbm5vdCRuZXd4DQphbm5vdCR5IDwtIGFubm90JG5ld3kNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTI1LGZpZy5oZWlnaHQ9MTUsIGVjaG89RkFMU0UsIG91dC53aWR0aD0iNTAlIn0NCiNQMzYgPSBjcmVhdGVQYWxldHRlKGxlbmd0aChyb3duYW1lcyh0KHJlcyRiZXRhKSkpLCAgYygiI2ZmMDAwMCIsICIjMDBmZjAwIiwgIiMwMDAwZmYiKSkNCg0KeGxpbSA8LSBtYXgocmVzQHBoZW5vRGF0YUBkYXRhJHgpDQp5bGltIDwtIG1heChyZXNAcGhlbm9EYXRhQGRhdGEkeSkNCg0KaW5kIDwtIHBEYXRhKHJlcykkc2xpZGVfbmFtZSA9PSAiaHVfa2lkbmV5XzAwMSINCnJlczIgPC0gcmVzWyxpbmRdDQoNCiNhIDwtIHJlczJbIXJlczJAcGhlbm9EYXRhQGRhdGFbWyJiZXRhIl1dPT0wXQ0KDQojc2xpZGVfbGlzdFtbNF1dIDwtIGZsaXBZKHNsaWRlX2xpc3RbWzRdXSkNCg0KZmx1b3IgPC0gZmx1b3JMZWdlbmQob3ZlcmxheSA9IHNsaWRlX2xpc3RbWzRdXSwgbnJvdyA9IDQsIHRleHRTaXplID0gNSwgYWxwaGEgPSAwLjI1KQ0KDQppbSA8LSBwbG90U3BhdGlhbE92ZXJsYXkob3ZlcmxheSA9IHNsaWRlX2xpc3RbWzRdXSwgbGVnZW5kID0gRkFMU0UsIGhpUmVzID0gRkFMU0UsIGNvcm5lciA9ICJib3R0b21yaWdodCIsIA0KICAgICAgICAgICAgICAgICAgIHNjYWxlQmFyV2lkdGggPSAwLjUsIHRleHREaXN0YW5jZSA9IDUsIHNjYWxlQmFyQ29sb3IgPSAid2hpdGUiLCBpbWFnZSA9IFRSVUUpICsgDQogIGdncGxvdDI6OmxhYnModGl0bGUgPSBwYXN0ZSgiV2hvbGUgc2xpZGUgT01FLVRJRkYiLCBzbGlkZU5hbWUoc2xpZGVfbGlzdFtbNF1dKSkpDQoNCiNwbG90KDAsIHhsaW0gPSBjKDAsIHhsaW0rNDAwMDApLCB5bGltID0gYygwLCB5bGltKSwgYXhlcyA9IEZBTFNFKQ0KDQpjb3dwbG90OjpnZ2RyYXcoKSArDQogIGNvd3Bsb3Q6OmRyYXdfcGxvdChpbSwgeCA9IC0wLjMsIHkgPSAwKSArDQogIGNvd3Bsb3Q6OmRyYXdfcGxvdChmbHVvciwgc2NhbGUgPSAwLjEyLCB4ID0gMC4xLCB5ID0gMCkgIysgIywgc2NhbGUgPSAwLjEyLCB4ID0gLTAuMywgeSA9IC0wLjI1KQ0KDQoNCmZsb3JldHMoDQogIHggPSByZXMyQHBoZW5vRGF0YUBkYXRhJHgsIyArIDQwMDAwLA0KICB5ID0gcmVzMkBwaGVub0RhdGFAZGF0YSR5LA0KICBjb2wgPSBQMzYsDQogICNjb2wgPSAxOmxlbmd0aChyb3duYW1lcyh0KGEkYmV0YSkpKSwNCiAgeGxpbSA9IGMoMCx4bGltKzQwMDAwKSwNCiAgeWxpbSA9IGMoMCx5bGltKSwNCiAgYiA9IHQoYSRiZXRhKSwNCiAgcmVzY2FsZS5ieS5zcXJ0ID0gVFJVRSwNCiAgYWRkID0gRkFMU0UsDQogIGNleCA9IDEsIHhsYWIgPSAiIiwgeWxhYiA9ICIiKQ0KDQpsZWdlbmQoInJpZ2h0IiwNCiAgICAgICAjZmlsbCA9IDE6bGVuZ3RoKHJvd25hbWVzKHQoYSRiZXRhKSkpLA0KICAgICAgIGZpbGwgPSBQMzYsDQogICAgICAgbGVnZW5kID0gcm93bmFtZXModChhJGJldGEpKSwgY2V4ID0gMS41KQ0KYGBgDQoNCiMgOS40IGNvbWJpbmluZyBjZWxsdHlwZXMNCg0KV2hlbiB0d28gY2VsbCB0eXBlcyBhcmUgdG9vIHNpbWlsYXIsIHRoZSBlc3RpbWF0aW9uIG9mIHRoZWlyIGFidW5kYW5jZXMNCmJlY29tZXMgdW5zdGFibGUuIEhvd2V2ZXIsIHRoZWlyIHN1bSBjYW4gc3RpbGwgYmUgZXN0aW1hdGVkIGVhc2lseS4gVGhlDQpmdW5jdGlvbiAiY29sbGFwc2VDZWxsVHlwZXMiIHRha2VzIGEgZGVjb252b2x1dGlvbiByZXN1bHRzIG9iamVjdCBhbmQNCmNvbGxhcHNlcyBhbnkgY29sc2VseS1yZWxhdGVkIGNlbGwgdHlwZXMgeW91IHRlbGwgaXQgdG8NCg0KYGBge3J9DQptYXRjaGluZyA9IGxpc3QoKQ0KbWF0Y2hpbmckbXllbG9pZCA9IGMoICJtYWNyb3BoYWdlcyIsICJtb25vY3l0ZXMiLCAibURDcyIpDQptYXRjaGluZyRULk5LID0gYygiQ0Q0LlQuY2VsbHMiLCJDRDguVC5jZWxscyIsICJUcmVnIiwgIk5LIikNCm1hdGNoaW5nJEIgPSBjKCJCIikNCm1hdGNoaW5nJG1hc3QgPSBjKCJtYXN0IikNCm1hdGNoaW5nJG5ldXRyb3BoaWxzID0gYygibmV1dHJvcGhpbHMiKQ0KbWF0Y2hpbmckc3Ryb21hID0gYygiZW5kb3RoZWxpYWwuY2VsbHMiLCAiZmlicm9ibGFzdHMiKQ0KDQpjb2xsYXBzZWQgPSBjb2xsYXBzZUNlbGxUeXBlcyhmaXQgPSByZXN0aWxzLCBtYXRjaGluZyA9IG1hdGNoaW5nKQ0KDQpoZWF0bWFwKGNvbGxhcHNlZCRiZXRhLCBjZXhSb3cgPSAwLjg1LCBjZXhDb2wgPSAwLjc1KQ0KYGBgDQoNCiMgTGlnYW5kLXJlY2VwdG9yIGFuYWx5c2UNCg0KYGBge3J9DQojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJzYWV5c2xhYi9uaWNoZW5ldHIiKQ0KI2luc3RhbGwucGFja2FnZXMoJ2NpcmNsaXplJykNCmxpYnJhcnkobmljaGVuZXRyKQ0KbGlicmFyeShTZXVyYXQpDQpsaWJyYXJ5KGNpcmNsaXplKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsZmlnLmhlaWdodD0xMH0NCiMgR2V0IGNvdW50cyBkYXRhDQpjb3VudHMgPC0gYXMuZGF0YS5mcmFtZSh0YXJnZXRfRGF0YUBhc3NheURhdGFbWyJxX25vcm0iXV0pDQpjb3VudHMkWCA8LSByb3duYW1lcyhjb3VudHMpDQojcm93bmFtZXMoY291bnRzKSA8LSBOVUxMDQojY291bnRzIDwtIGFzLm1hdHJpeChjb3VudHMpDQoNCiMgR2V0IG1ldGEgZGF0YQ0KbWV0YSA8LSBwRGF0YSh0YXJnZXRfRGF0YSkNCm1ldGEgPC0gbWV0YVtjKCJBTk4xIiwgIkFOTjIiLCAic2xpZGVfbmFtZSIpXQ0KbWV0YSRTYW1wbGVJRCA8LSBwcm90b2NvbERhdGEodGFyZ2V0X0RhdGEpW1siU2FtcGxlSUQiXV0NCm1ldGEkcm9pIDwtIHByb3RvY29sRGF0YSh0YXJnZXRfRGF0YSlbWyJyb2kiXV0NCg0KIyBjcmVhdGUgU2V1cmF0IGZvciBiZXR0ZXIgbmljaGUgY29vcGVyYXRpb24NClNldXJhdC5EYXRhIDwtIENyZWF0ZVNldXJhdE9iamVjdChjb3VudHMgPSBjb3VudHMsIG1ldGEuZGF0YSA9IG1ldGEpIA0KDQojIyBDaGVjayBtZXRhIGRhdGENCiNTZXVyYXQuRGF0YUBtZXRhLmRhdGEgJT4lIGhlYWQoKQ0KI1NldXJhdC5EYXRhQG1ldGEuZGF0YSRSZWdpb24gJT4lIHRhYmxlKCkgDQoNCiMjIENoYW5nZSBpZGVudHMgdG8gY29ycmVjdCBhbm5vdGF0aW9uDQpTZXVyYXQuRGF0YSA8LSBTZXRJZGVudChTZXVyYXQuRGF0YSwgdmFsdWUgPSAiQU5OMSIpDQojU2V1cmF0LkRhdGFAYWN0aXZlLmlkZW50DQoNCiMjIyMjIyMgUmVhZCBpbiBsaWdhbmQtdGFyZ2V0IHByaW9yIG1vZGVsLCBsaWdhbmQtcmVjZXB0b3IgbmV0d29yayBhbmQgd2VpZ2h0ZWQgaW50ZWdyYXRlZCBuZXR3b3Jrcw0KbGlnYW5kX3RhcmdldF9tYXRyaXggPSByZWFkUkRTKHVybCgiaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZC8zMjYwNzU4L2ZpbGVzL2xpZ2FuZF90YXJnZXRfbWF0cml4LnJkcyIpKQ0KI2xpZ2FuZF90YXJnZXRfbWF0cml4WzE6NSwxOjVdICMgdGFyZ2V0IGdlbmVzIGluIHJvd3MsIGxpZ2FuZHMgaW4gY29sdW1ucw0KDQpscl9uZXR3b3JrID0gcmVhZFJEUyh1cmwoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMzI2MDc1OC9maWxlcy9scl9uZXR3b3JrLnJkcyIpKQ0KI2hlYWQobHJfbmV0d29yaykNCg0Kd2VpZ2h0ZWRfbmV0d29ya3MgPSByZWFkUkRTKHVybCgiaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZC8zMjYwNzU4L2ZpbGVzL3dlaWdodGVkX25ldHdvcmtzLnJkcyIpKQ0Kd2VpZ2h0ZWRfbmV0d29ya3NfbHIgPSB3ZWlnaHRlZF9uZXR3b3JrcyRscl9zaWcgJT4lIGlubmVyX2pvaW4obHJfbmV0d29yayAlPiUgZGlzdGluY3QoZnJvbSx0byksIGJ5ID0gYygiZnJvbSIsInRvIikpDQojaGVhZCh3ZWlnaHRlZF9uZXR3b3JrcyRscl9zaWcpICMgaW50ZXJhY3Rpb25zIGFuZCB0aGVpciB3ZWlnaHRzIGluIHRoZSBsaWdhbmQtcmVjZXB0b3IgKyBzaWduYWxpbmcgbmV0d29yaw0KDQoNCiMjIyMgRGVmaW5lIGEgc2VuZGVyIGFuZCByZWNlaXZlciBwb3B1bGF0aW9uIGFuZCBkZXRlcm1pbmUgd2hpY2ggZ2VuZXMgYXJlIGV4cHJlc3NlZCBpbiBib3RoIHBvcHVsYXRpb25zDQoNCiMjIHJlY2VpdmVyDQpyZWNlaXZlciA9IGMoIkNEMTArIikNCmV4cHJlc3NlZF9nZW5lc19yZWNlaXZlciA9IGdldF9leHByZXNzZWRfZ2VuZXMocmVjZWl2ZXIsIFNldXJhdC5EYXRhLCBwY3QgPSAwLjEwKQ0KYmFja2dyb3VuZF9leHByZXNzZWRfZ2VuZXMgPSBleHByZXNzZWRfZ2VuZXNfcmVjZWl2ZXIgJT4lIC5bLiAlaW4lIHJvd25hbWVzKGxpZ2FuZF90YXJnZXRfbWF0cml4KV0NCg0KIyMgc2VuZGVyDQpzZW5kZXJfY2VsbHR5cGVzID0gYygiUGFuQ0srIikNCmxpc3RfZXhwcmVzc2VkX2dlbmVzX3NlbmRlciA9IHNlbmRlcl9jZWxsdHlwZXMgJT4lIHVuaXF1ZSgpICU+JSBsYXBwbHkoZ2V0X2V4cHJlc3NlZF9nZW5lcywgU2V1cmF0LkRhdGEsIDAuMTApICMgbGFwcGx5IHRvIGdldCB0aGUgZXhwcmVzc2VkIGdlbmVzIG9mIGV2ZXJ5IHNlbmRlciBjZWxsIHR5cGUgc2VwYXJhdGVseSBoZXJlDQpleHByZXNzZWRfZ2VuZXNfc2VuZGVyID0gbGlzdF9leHByZXNzZWRfZ2VuZXNfc2VuZGVyICU+JSB1bmxpc3QoKSAlPiUgdW5pcXVlKCkNCg0KIyMjIyBJbXBvcnQgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGluIHJlY2lldmVyIHBvcHVsYXRpb25zDQpnZW5lc2V0Lm9pIDwtIHRvcF9nDQoNCiMjIyMjICBEZWZpbmUgYSBzZXQgb2YgbGlnYW5kcyBpbiBzZW5kZXIgcG9wdWxhdGlvbnMNCmxpZ2FuZHMgPSBscl9uZXR3b3JrICU+JSBwdWxsKGZyb20pICU+JSB1bmlxdWUoKQ0KcmVjZXB0b3JzID0gbHJfbmV0d29yayAlPiUgcHVsbCh0bykgJT4lIHVuaXF1ZSgpDQoNCmV4cHJlc3NlZF9saWdhbmRzID0gaW50ZXJzZWN0KGxpZ2FuZHMsZXhwcmVzc2VkX2dlbmVzX3NlbmRlcikNCmV4cHJlc3NlZF9yZWNlcHRvcnMgPSBpbnRlcnNlY3QocmVjZXB0b3JzLGV4cHJlc3NlZF9nZW5lc19yZWNlaXZlcikNCg0KcG90ZW50aWFsX2xpZ2FuZHMgPSBscl9uZXR3b3JrICU+JSBmaWx0ZXIoZnJvbSAlaW4lIGV4cHJlc3NlZF9saWdhbmRzICYgdG8gJWluJSBleHByZXNzZWRfcmVjZXB0b3JzKSAlPiUgcHVsbChmcm9tKSAlPiUgdW5pcXVlKCkNCg0KDQojIyMjIyMjIyMjIyMjIw0KIyMjIyMjIyMjIyMgUGVyZm9ybSBOaWNoZW5ldCBhbmFseXNpcw0KDQoNCmxpZ2FuZF9hY3Rpdml0aWVzID0gcHJlZGljdF9saWdhbmRfYWN0aXZpdGllcyhnZW5lc2V0ID0gZ2VuZXNldC5vaSwgYmFja2dyb3VuZF9leHByZXNzZWRfZ2VuZXMgPSBiYWNrZ3JvdW5kX2V4cHJlc3NlZF9nZW5lcywgbGlnYW5kX3RhcmdldF9tYXRyaXggPSBsaWdhbmRfdGFyZ2V0X21hdHJpeCwgcG90ZW50aWFsX2xpZ2FuZHMgPSBwb3RlbnRpYWxfbGlnYW5kcykNCg0KbGlnYW5kX2FjdGl2aXRpZXMgPSBsaWdhbmRfYWN0aXZpdGllcyAlPiUgYXJyYW5nZSgtcGVhcnNvbikgDQojbGlnYW5kX2FjdGl2aXRpZXMgPSBsaWdhbmRfYWN0aXZpdGllcyAlPiUgYXJyYW5nZSgtcGVhcnNvbikgJT4lIG11dGF0ZShyYW5rID0gcmFuayhkZXNjKHBlYXJzb24pKSkNCg0KDQojIyMjIyMjIyMjIyMjIw0KIyMjIyMjIyBIaXN0b2dyYW0gb2YgbGlnYW5kIGFjdGl2aXRpZXMNCg0KcF9oaXN0X2xpZ19hY3Rpdml0eSA9IGdncGxvdChsaWdhbmRfYWN0aXZpdGllcywgYWVzKHg9cGVhcnNvbikpICsgDQogIGdlb21faGlzdG9ncmFtKGNvbG9yPSJibGFjayIsIGZpbGw9ImRhcmtvcmFuZ2UiKSAgKyANCiAgIyBnZW9tX2RlbnNpdHkoYWxwaGE9LjEsIGZpbGw9Im9yYW5nZSIpICsNCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdD1taW4obGlnYW5kX2FjdGl2aXRpZXMgJT4lIHRvcF9uKDIwLCBwZWFyc29uKSAlPiUgcHVsbChwZWFyc29uKSkpLCBjb2xvcj0icmVkIiwgbGluZXR5cGU9ImRhc2hlZCIsIHNpemU9MSkgKyANCiAgbGFicyh4PSJsaWdhbmQgYWN0aXZpdHkgKFBDQykiLCB5ID0gIiMgbGlnYW5kcyIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQpwX2hpc3RfbGlnX2FjdGl2aXR5DQoNCg0KIyMjIERvdCBibG90IG9mIHRvcCBsaWdhbmRzIHNwbGl0IGJ5IHJlZ2lvbg0KDQpiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMgPSBjKGxpZ2FuZF9hY3Rpdml0aWVzICU+JSB0b3BfbigyMCwgcGVhcnNvbikgJT4lIGFycmFuZ2UoLXBlYXJzb24pICU+JSBwdWxsKHRlc3RfbGlnYW5kKSAlPiUgdW5pcXVlKCkpDQoNCiMgdGVzdCA8LSBTZXVyYXQuRGF0YQ0KIyB5ZWV0IDwtIGMoIldOVDNBIikNCiMgZGF0YSgicGJtY19zbWFsbCIpDQojIHRlc3RbWyJSTkEiXV1AdmFyLmZlYXR1cmVzIDwtIGJlc3RfdXBzdHJlYW1fbGlnYW5kcw0KIyAjZGF0YS5mcmFtZShyb3cubmFtZXMgPSByb3duYW1lcyh0ZXN0W1siUk5BIl1dKSkNCiMgDQojIERvdFBsb3QodGVzdCwgZmVhdHVyZXMgPSBiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMsIGNvbHMgPSAiUmRZbEJ1IikgKyBSb3RhdGVkQXhpcygpDQoNCiMjIGVsc2UNCg0KYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYgPSBiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMgJT4lIGxhcHBseShnZXRfd2VpZ2h0ZWRfbGlnYW5kX3RhcmdldF9saW5rcyxnZW5lc2V0ID0gZ2VuZXNldC5vaSwgbGlnYW5kX3RhcmdldF9tYXRyaXggPSBsaWdhbmRfdGFyZ2V0X21hdHJpeCwgbiA9IDI1MCkgJT4lIGJpbmRfcm93cygpDQphY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rcyA9IHByZXBhcmVfbGlnYW5kX3RhcmdldF92aXN1YWxpemF0aW9uKGxpZ2FuZF90YXJnZXRfZGYgPSBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiwgbGlnYW5kX3RhcmdldF9tYXRyaXggPSBsaWdhbmRfdGFyZ2V0X21hdHJpeCwgY3V0b2ZmID0gMC4yNSkNCg0Kb3JkZXJfbGlnYW5kcyA9IGludGVyc2VjdChiZXN0X3Vwc3RyZWFtX2xpZ2FuZHMsIGNvbG5hbWVzKGFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzKSkgJT4lIHJldigpDQpvcmRlcl90YXJnZXRzID0gYWN0aXZlX2xpZ2FuZF90YXJnZXRfbGlua3NfZGYkdGFyZ2V0ICU+JSB1bmlxdWUoKQ0KdmlzX2xpZ2FuZF90YXJnZXQgPSBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc1tvcmRlcl90YXJnZXRzLG9yZGVyX2xpZ2FuZHNdICU+JSB0KCkNCg0KcF9saWdhbmRfdGFyZ2V0X25ldHdvcmsgPSB2aXNfbGlnYW5kX3RhcmdldCAlPiUgbWFrZV9oZWF0bWFwX2dncGxvdCgiUHJpb3JpdGl6ZWQgUGFuQ0srIGxpZ2FuZHMiLCJDRDEwKyBwb3RlbnRpYWwgcmVjZXB0b3IgY2VsbHMiLCBjb2xvciA9ICJwdXJwbGUiLGxlZ2VuZF9wb3NpdGlvbiA9ICJ0b3AiLCB4X2F4aXNfcG9zaXRpb24gPSAidG9wIixsZWdlbmRfdGl0bGUgPSAiUmVndWxhdG9yeSBwb3RlbnRpYWwiKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICJ3aGl0ZXNtb2tlIiwgIGhpZ2ggPSAicHVycGxlIiwgYnJlYWtzID0gYygwLDAuMDA1LDAuMDEpKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiaXRhbGljIikpDQoNCnBfbGlnYW5kX3RhcmdldF9uZXR3b3JrDQoNCiMjIGNpcmNvcw0KDQojIFFDIG9uIHF1YW50aWxlDQpjdXRvZmZfaW5jbHVkZV9hbGxfbGlnYW5kcyA9IGFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzX2RmJHdlaWdodCAlPiUgcXVhbnRpbGUoMC42NikNCmFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzX2RmX2NpcmNvcyA9IGFjdGl2ZV9saWdhbmRfdGFyZ2V0X2xpbmtzX2RmICU+JSBmaWx0ZXIod2VpZ2h0ID4gY3V0b2ZmX2luY2x1ZGVfYWxsX2xpZ2FuZHMpDQoNCmxpZ2FuZHNfdG9fcmVtb3ZlID0gc2V0ZGlmZihhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiRsaWdhbmQgJT4lIHVuaXF1ZSgpLCBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZl9jaXJjb3MkbGlnYW5kICU+JSB1bmlxdWUoKSkNCnRhcmdldHNfdG9fcmVtb3ZlID0gc2V0ZGlmZihhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiR0YXJnZXQgJT4lIHVuaXF1ZSgpLCBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZl9jaXJjb3MkdGFyZ2V0ICU+JSB1bmlxdWUoKSkNCiAgDQpjaXJjb3NfbGlua3MgPSBhY3RpdmVfbGlnYW5kX3RhcmdldF9saW5rc19kZiAlPiUgZmlsdGVyKCF0YXJnZXQgJWluJSB0YXJnZXRzX3RvX3JlbW92ZSAmIWxpZ2FuZCAlaW4lIGxpZ2FuZHNfdG9fcmVtb3ZlKQ0KDQojIENvbG9yDQpncmlkX2NvbF9saWdhbmQgPWMoIlBhbkNLKyIgPSAicmVkIg0KICAgICAgICAgICAgICAgICAgIywiR2VuZXJhbCIgPSAiZ3JlZW4iDQogICAgICAgICAgICAgICAgICAgKQ0KZ3JpZF9jb2xfdGFyZ2V0ID1jKCJDRDEwKyIgPSAiZ3JlZW4iKQ0KDQpncmlkX2NvbF90YmxfbGlnYW5kID0gdGliYmxlKGxpZ2FuZF90eXBlID0gZ3JpZF9jb2xfbGlnYW5kICU+JSBuYW1lcygpLCBjb2xvcl9saWdhbmRfdHlwZSA9IGdyaWRfY29sX2xpZ2FuZCkNCmdyaWRfY29sX3RibF90YXJnZXQgPSB0aWJibGUodGFyZ2V0X3R5cGUgPSBncmlkX2NvbF90YXJnZXQgJT4lIG5hbWVzKCksIGNvbG9yX3RhcmdldF90eXBlID0gZ3JpZF9jb2xfdGFyZ2V0KQ0KDQpjaXJjb3NfbGlua3MgPSBjaXJjb3NfbGlua3MgJT4lIG11dGF0ZShsaWdhbmQgPSBwYXN0ZShsaWdhbmQsIiAiKSkgIyBleHRyYSBzcGFjZTogbWFrZSBhIGRpZmZlcmVuY2UgYmV0d2VlbiBhIGdlbmUgYXMgbGlnYW5kIGFuZCBhIGdlbmUgYXMgdGFyZ2V0IQ0KY2lyY29zX2xpbmtzID0gY2lyY29zX2xpbmtzICU+JSBpbm5lcl9qb2luKGdyaWRfY29sX3RibF9saWdhbmQsIGJ5ID0gY2hhcmFjdGVyKCkpICU+JSBpbm5lcl9qb2luKGdyaWRfY29sX3RibF90YXJnZXQsIGJ5ID0gY2hhcmFjdGVyKCkpDQpsaW5rc19jaXJjbGUgPSBjaXJjb3NfbGlua3MgJT4lIHNlbGVjdChsaWdhbmQsdGFyZ2V0LCB3ZWlnaHQpDQoNCmxpZ2FuZF9jb2xvciA9IGNpcmNvc19saW5rcyAlPiUgZGlzdGluY3QobGlnYW5kLGNvbG9yX2xpZ2FuZF90eXBlKQ0KZ3JpZF9saWdhbmRfY29sb3IgPSBsaWdhbmRfY29sb3IkY29sb3JfbGlnYW5kX3R5cGUgJT4lIHNldF9uYW1lcyhsaWdhbmRfY29sb3IkbGlnYW5kKQ0KdGFyZ2V0X2NvbG9yID0gY2lyY29zX2xpbmtzICU+JSBkaXN0aW5jdCh0YXJnZXQsY29sb3JfdGFyZ2V0X3R5cGUpDQpncmlkX3RhcmdldF9jb2xvciA9IHRhcmdldF9jb2xvciRjb2xvcl90YXJnZXRfdHlwZSAlPiUgc2V0X25hbWVzKHRhcmdldF9jb2xvciR0YXJnZXQpDQoNCmdyaWRfY29sID0gYyhncmlkX2xpZ2FuZF9jb2xvcixncmlkX3RhcmdldF9jb2xvcikNCg0KIyBnaXZlIHRoZSBvcHRpb24gdGhhdCBsaW5rcyBpbiB0aGUgY2lyY29zIHBsb3Qgd2lsbCBiZSB0cmFuc3BhcmFudCB+IGxpZ2FuZC10YXJnZXQgcG90ZW50aWFsIHNjb3JlDQp0cmFuc3BhcmVuY3kgPSBjaXJjb3NfbGlua3MgJT4lIG11dGF0ZSh3ZWlnaHQgPSh3ZWlnaHQtbWluKHdlaWdodCkpLyhtYXgod2VpZ2h0KS1taW4od2VpZ2h0KSkpICU+JSBtdXRhdGUodHJhbnNwYXJlbmN5ID0gMS13ZWlnaHQpICU+JSAuJHRyYW5zcGFyZW5jeSANCg0KIyBTcGVjaWZpYyBvcmRlciB3aXRoIGdhcHMgaWYgbmVlZGVkDQp0YXJnZXRfb3JkZXIgPSBjaXJjb3NfbGlua3MkdGFyZ2V0ICU+JSB1bmlxdWUoKQ0KI2xpZ2FuZF9vcmRlciA9IGMoQ0FGX3NwZWNpZmljX2xpZ2FuZHMsZ2VuZXJhbF9saWdhbmRzLGVuZG90aGVsaWFsX3NwZWNpZmljX2xpZ2FuZHMpICU+JSBjKHBhc3RlKC4sIiAiKSkgJT4lIGludGVyc2VjdChjaXJjb3NfbGlua3MkbGlnYW5kKQ0KbGlnYW5kX29yZGVyID0gYygpICU+JSBjKHBhc3RlKC4sIiAiKSkgJT4lIGludGVyc2VjdChjaXJjb3NfbGlua3MkbGlnYW5kKQ0Kb3JkZXIgPSBjKGxpZ2FuZF9vcmRlcix0YXJnZXRfb3JkZXIpDQoNCndpZHRoX3NhbWVfY2VsbF9zYW1lX2xpZ2FuZF90eXBlID0gMC41DQp3aWR0aF9kaWZmZXJlbnRfY2VsbCA9IDYNCndpZHRoX2xpZ2FuZF90YXJnZXQgPSAxNQ0Kd2lkdGhfc2FtZV9jZWxsX3NhbWVfdGFyZ2V0X3R5cGUgPSAwLjUNCg0KZ2FwcyA9IGMoDQogICMgd2lkdGhfbGlnYW5kX3RhcmdldCwNCiAgcmVwKHdpZHRoX3NhbWVfY2VsbF9zYW1lX2xpZ2FuZF90eXBlLCB0aW1lcyA9IChjaXJjb3NfbGlua3MgJT4lIGZpbHRlcihsaWdhbmRfdHlwZSA9PSAiUGFuQ0srIikgJT4lIGRpc3RpbmN0KGxpZ2FuZCkgJT4lIG5yb3coKSAtMSkpLA0KICB3aWR0aF9kaWZmZXJlbnRfY2VsbCwNCiAgIyByZXAod2lkdGhfc2FtZV9jZWxsX3NhbWVfbGlnYW5kX3R5cGUsIHRpbWVzID0gKGNpcmNvc19saW5rcyAlPiUgZmlsdGVyKGxpZ2FuZF90eXBlID09ICJHZW5lcmFsIikgJT4lIGRpc3RpbmN0KGxpZ2FuZCkgJT4lIG5yb3coKSAtMSkpLA0KICAjIHdpZHRoX2RpZmZlcmVudF9jZWxsLA0KICAjcmVwKHdpZHRoX3NhbWVfY2VsbF9zYW1lX2xpZ2FuZF90eXBlLCB0aW1lcyA9IChjaXJjb3NfbGlua3MgJT4lIGZpbHRlcihsaWdhbmRfdHlwZSA9PSAiRW5kb3RoZWxpYWwtc3BlY2lmaWMiKSAlPiUgZGlzdGluY3QobGlnYW5kKSAlPiUgbnJvdygpIC0xKSksIA0KICAjd2lkdGhfbGlnYW5kX3RhcmdldCwNCiAgcmVwKHdpZHRoX3NhbWVfY2VsbF9zYW1lX3RhcmdldF90eXBlLCB0aW1lcyA9IChjaXJjb3NfbGlua3MgJT4lIGZpbHRlcih0YXJnZXRfdHlwZSA9PSAiQ0QxMCsiKSAlPiUgZGlzdGluY3QodGFyZ2V0KSAlPiUgbnJvdygpIC0xKSksDQogIHdpZHRoX2xpZ2FuZF90YXJnZXQNCiAgKQ0KDQojY2lyY29zLnBhcihnYXAuZGVncmVlID0gZ2FwcykNCg0KY2lyY29zLnBhcihSRVNFVCA9IFRSVUUpDQoNCmNob3JkRGlhZ3JhbShsaW5rc19jaXJjbGUsIA0KICAgICAgICAgICAgIGRpcmVjdGlvbmFsID0gMSwNCiAgICAgICAgICAgICAjb3JkZXI9b3JkZXIsDQogICAgICAgICAgICAgbGluay5zb3J0ID0gVFJVRSwgDQogICAgICAgICAgICAgbGluay5kZWNyZWFzaW5nID0gRkFMU0UsIA0KICAgICAgICAgICAgIGdyaWQuY29sID0gZ3JpZF9jb2wsDQogICAgICAgICAgICAgdHJhbnNwYXJlbmN5ID0gdHJhbnNwYXJlbmN5LCANCiAgICAgICAgICAgICBkaWZmSGVpZ2h0ID0gMC4wMDUsIA0KICAgICAgICAgICAgIGRpcmVjdGlvbi50eXBlID0gYygiZGlmZkhlaWdodCIsICJhcnJvd3MiKSwNCiAgICAgICAgICAgICBsaW5rLmFyci50eXBlID0gImJpZy5hcnJvdyIsIA0KICAgICAgICAgICAgIGxpbmsudmlzaWJsZSA9IGxpbmtzX2NpcmNsZSR3ZWlnaHQgPj0gY3V0b2ZmX2luY2x1ZGVfYWxsX2xpZ2FuZHMsDQogICAgICAgICAgICAgYW5ub3RhdGlvblRyYWNrID0gImdyaWQiLA0KICAgICAgICAgICAgIHByZUFsbG9jYXRlVHJhY2tzID0gbGlzdCh0cmFjay5oZWlnaHQgPSAwLjA3NSkpDQogICAgICAgICAgICAgI3ByZUFsbG9jYXRlVHJhY2tzID0gbGlzdCh0cmFjay5oZWlnaHQgPSBtYXgoc3Ryd2lkdGgodW5saXN0KGRpbW5hbWVzKGxpbmtzX2NpcmNsZSkpKSkpKSAgDQogICAgICAgICAgICAgDQojIHdlIGdvIGJhY2sgdG8gdGhlIGZpcnN0IHRyYWNrIGFuZCBjdXN0b21pemUgc2VjdG9yIGxhYmVscw0KY2lyY29zLnRyYWNrKHRyYWNrLmluZGV4ID0gMSwgcGFuZWwuZnVuID0gZnVuY3Rpb24oeCwgeSkgew0KICAgIGNpcmNvcy50ZXh0KENFTExfTUVUQSR4Y2VudGVyLCBDRUxMX01FVEEkeWxpbVsxXSwgQ0VMTF9NRVRBJHNlY3Rvci5pbmRleCwNCiAgICAgICAgZmFjaW5nID0gImNsb2Nrd2lzZSIsIG5pY2VGYWNpbmcgPSBUUlVFLCBhZGogPSBjKDAsIDAuNTUpLCBjZXggPSAxKQ0KfSwgYmcuYm9yZGVyID0gTkEpIA0KDQpsZWdlbmQoImJvdHRvbWxlZnQiLCANCiAgICAgICB0aXRsZSA9ICJDZWxsIHNwZWNpZmljIiwNCiAgICAgICBjZXg9IDMsIA0KICAgICAgIGxlZ2VuZCA9IHVuaXF1ZShjKGNpcmNvc19saW5rcyRsaWdhbmRfdHlwZSwgdW5pcXVlKGNpcmNvc19saW5rcyR0YXJnZXRfdHlwZSkpKSwNCiAgICAgICBmaWxsID0gdW5pcXVlKGMoZ3JpZF9jb2wpKSkNCnRpdGxlKCJMaWdhbmQtcmVjZXB0b3IgY29ubmVjdGlvbnMiKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9MjUsZmlnLmhlaWdodD0xMH0NCiMgZ2V0IHRoZSBsaWdhbmQtcmVjZXB0b3IgbmV0d29yayBvZiB0aGUgdG9wLXJhbmtlZCBsaWdhbmRzDQpscl9uZXR3b3JrX3RvcCA9IGxyX25ldHdvcmsgJT4lIGZpbHRlcihmcm9tICVpbiUgYmVzdF91cHN0cmVhbV9saWdhbmRzICYgdG8gJWluJSBleHByZXNzZWRfcmVjZXB0b3JzKSAlPiUgZGlzdGluY3QoZnJvbSx0bykNCmJlc3RfdXBzdHJlYW1fcmVjZXB0b3JzID0gbHJfbmV0d29ya190b3AgJT4lIHB1bGwodG8pICU+JSB1bmlxdWUoKQ0KDQojICMgZ2V0IHRoZSB3ZWlnaHRzIG9mIHRoZSBsaWdhbmQtcmVjZXB0b3IgaW50ZXJhY3Rpb25zIGFzIHVzZWQgaW4gdGhlIE5pY2hlTmV0IG1vZGVsDQojIHdlaWdodGVkX25ldHdvcmtzID0gcmVhZFJEUyh1cmwoImh0dHBzOi8vemVub2RvLm9yZy9yZWNvcmQvMzI2MDc1OC9maWxlcy93ZWlnaHRlZF9uZXR3b3Jrcy5yZHMiKSkNCmxyX25ldHdvcmtfdG9wX2RmID0gd2VpZ2h0ZWRfbmV0d29ya3MkbHJfc2lnICU+JSBmaWx0ZXIoZnJvbSAlaW4lIGJlc3RfdXBzdHJlYW1fbGlnYW5kcyAmIHRvICVpbiUgYmVzdF91cHN0cmVhbV9yZWNlcHRvcnMpDQoNCiMgY29udmVydCB0byBhIG1hdHJpeA0KbHJfbmV0d29ya190b3BfZGYgPSBscl9uZXR3b3JrX3RvcF9kZiAlPiUgc3ByZWFkKCJmcm9tIiwid2VpZ2h0IixmaWxsID0gMCkNCmxyX25ldHdvcmtfdG9wX21hdHJpeCA9IGxyX25ldHdvcmtfdG9wX2RmICU+JSBzZWxlY3QoLXRvKSAlPiUgYXMubWF0cml4KCkgJT4lIG1hZ3JpdHRyOjpzZXRfcm93bmFtZXMobHJfbmV0d29ya190b3BfZGYkdG8pDQoNCiMgcGVyZm9ybSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyB0byBvcmRlciB0aGUgbGlnYW5kcyBhbmQgcmVjZXB0b3JzDQpkaXN0X3JlY2VwdG9ycyA9IGRpc3QobHJfbmV0d29ya190b3BfbWF0cml4LCBtZXRob2QgPSAiYmluYXJ5IikNCmhjbHVzdF9yZWNlcHRvcnMgPSBoY2x1c3QoZGlzdF9yZWNlcHRvcnMsIG1ldGhvZCA9ICJ3YXJkLkQyIikNCm9yZGVyX3JlY2VwdG9ycyA9IGhjbHVzdF9yZWNlcHRvcnMkbGFiZWxzW2hjbHVzdF9yZWNlcHRvcnMkb3JkZXJdDQoNCmRpc3RfbGlnYW5kcyA9IGRpc3QobHJfbmV0d29ya190b3BfbWF0cml4ICU+JSB0KCksIG1ldGhvZCA9ICJiaW5hcnkiKQ0KaGNsdXN0X2xpZ2FuZHMgPSBoY2x1c3QoZGlzdF9saWdhbmRzLCBtZXRob2QgPSAid2FyZC5EMiIpDQpvcmRlcl9saWdhbmRzX3JlY2VwdG9yID0gaGNsdXN0X2xpZ2FuZHMkbGFiZWxzW2hjbHVzdF9saWdhbmRzJG9yZGVyXQ0KDQoNCnZpc19saWdhbmRfcmVjZXB0b3JfbmV0d29yayA9IGxyX25ldHdvcmtfdG9wX21hdHJpeFtvcmRlcl9yZWNlcHRvcnMsIG9yZGVyX2xpZ2FuZHNfcmVjZXB0b3JdDQpwX2xpZ2FuZF9yZWNlcHRvcl9uZXR3b3JrID0gdmlzX2xpZ2FuZF9yZWNlcHRvcl9uZXR3b3JrICU+JSB0KCkgJT4lIG1ha2VfaGVhdG1hcF9nZ3Bsb3QoIlByaW9yaXRpemVkIFBhbkNLK2xpZ2FuZHMiLCJSZWNlcHRvcnMgZXhwcmVzc2VkIGJ5IENEMTArIiwgY29sb3IgPSAibWVkaXVtdmlvbGV0cmVkIiwgeF9heGlzX3Bvc2l0aW9uID0gInRvcCIsbGVnZW5kX3RpdGxlID0gIlByaW9yIGludGVyYWN0aW9uIHBvdGVudGlhbCIpDQpwX2xpZ2FuZF9yZWNlcHRvcl9uZXR3b3JrDQpgYGANCg0KIyBDTlYNCg0KYGBge3J9DQojQmlvY01hbmFnZXI6Omluc3RhbGwoImluZmVyY252IikNCmxpYnJhcnkoaW5mZXJjbnYpDQojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJhZXJpY2tzby9TcGF0aWFsSW5mZXJDTlYiKQ0KI2xpYnJhcnkoU3BhdGlhbEluZmVyQ05WKQ0KYGBgDQoNCmBgYHtyIGNyZWF0ZSBjbnYgZmlsZXNffQ0KZmlsZSA8LSByZWFkTGluZXMoUEtDRmlsZXMpDQpmaWxlIDwtIGdzdWIoJyAnLCAnJywNCiAgICAgICAgZ3N1YignIicsICcnLCBmaWxlKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgRm9ybWF0IGFuZCByZW1vdmUgc3BhY2UNCg0KZGlzcGxheSA8LSBmaWxlW2dyZXBsKCJEaXNwbGF5TmFtZSIsIGZpbGUsIGZpeGVkID0gVFJVRSldICAgICAgICAgIyBHcmFiIGRpc3BsYXkgbmFtZXMNCmRpc3BsYXkgPC0gZ3N1YignRGlzcGxheU5hbWUnLCAiIiwNCiAgICAgICAgICAgZ3N1YignIicsICIiLA0KICAgICAgICAgICBnc3ViKCc6JywgIiIsDQogICAgICAgICAgIGdzdWIoJywnLCAiIiwNCiAgICAgICAgICAgZ3N1YignICcsICIiLA0KICAgICAgICAgICBnc3ViKCdfMDEnLCAiIiwgZGlzcGxheSkpKSkpKSAgICAgICAgICAgICAgICAgICAgICAgICAgIyBHcmFiIG9ubHkgdGhlIG5hbWVzDQpkaXNwbGF5IDwtIHVuaXF1ZShkaXNwbGF5KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFJlbW92ZSBkdXBsaWNhdGVzDQoNCmNociA8LSBmaWxlW2dyZXAoIkdlbm9tZUNvb3JkaW5hdGVzIiwgZmlsZSkrMV0gICAgICAgICAgICAgICAgICAgICMgR2V0IHRoZSBjaHIgcG9zaXRpb25zIHVuZGVyIHRoZSBHZW5vbWVDb29yZGluYXRlcyBsaW5lDQpjaHIgPC1nc3ViKCcsJywgIiIsIGNocikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFJlbW92ZSB1bndhbnRlZCBzeW1ib2xzDQoNCnBvc2l0aW9ucyA8LSBkYXRhLmZyYW1lKA0KICBuYW1lID0gZGlzcGxheSwgDQogIGNociA9IGNocikNCnBvc2l0aW9ucyA8LSBwb3NpdGlvbnNbIWdyZXBsKCJUYXJnZXRTZXF1ZW5jZSIsIHBvc2l0aW9ucyRjaHIpLF0gICMgUmVtb3ZlIGVudHJpZXMgd2l0aG91dCBjb29yZGluYXRlcw0KDQpwb3NpdGlvbnMkY2hyIDwtIGdzdWIoJzonLCAnLScsIHBvc2l0aW9ucyRjaHIpICAgICAgICAgICAgICAgICAgICAjIEZvcm1hdCBmb3Igc3BsaXR0aW5nDQpwb3NpdGlvbnMkY2hyIDwtIHN0cl9zcGxpdF9maXhlZChwb3NpdGlvbnMkY2hyLCAiLSIsIDMpICAgICAgICAgICAjIFNwbGl0IGludG8gY2hyLCBiZWdpbiwgYW5kIGVuZCBwb3NpdGlvbg0KcG9zaXRpb25zIDwtIGFzLm1hdHJpeChwb3NpdGlvbnMpIA0KcG9zaXRpb25zIDwtIGFzLmRhdGEuZnJhbWUocG9zaXRpb25zKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTd2FwIHR5cGVzIHRvIHJlY29nbml6ZSBzcGxpdCBjb2x1bW5zIGFzIHNpbmd1bGFyIGNvbHVtbnMNCmNvbG5hbWVzKHBvc2l0aW9ucykgPC0gTlVMTA0Kcm93bmFtZXMocG9zaXRpb25zKSA8LSBOVUxMICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbmZlckNOViByZXF1aXJlcyBubyBjb2x1bW4gYW5kIHJvdyBuYW1lcw0KDQp3cml0ZS50YWJsZShwb3NpdGlvbnMsICJnZW5lX29yZGVyX0hzX1dUQV92MV9wa2MudHh0IiwgDQogICAgICAgICAgICBzZXAgPSAiXHQiLA0KICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSwgDQogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwgDQogICAgICAgICAgICByb3cubmFtZXMgPSBGQUxTRSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFdyaXRlIGF3YXkNCg0KY291bnRzIDwtIHRhcmdldF9EYXRhQGFzc2F5RGF0YVtbImV4cHJzIl1dDQp3cml0ZS50YWJsZShjb3VudHMsICJDb3VudHMudHN2Iiwgc2VwID0gIlx0IikgICAgICAgICAgICAgICAgICAgICMgTWFrZSByYXcgY291bnQgZmlsZQ0KDQphbm5vdGF0aW9uIDwtIHRhcmdldF9EYXRhQHBoZW5vRGF0YUBkYXRhDQphbm5vdGF0aW9uJFNhbXBsZUlEIDwtIHJvd25hbWVzKGFubm90YXRpb24pDQphbm5vdGF0aW9uIDwtIGFubm90YXRpb25bYygiU2FtcGxlSUQiLCAiQU5OMiIpXSAgICAgICAgICAgICAgICAgICMgU2VsZWN0IFNhbXBsZUlEIGFuZCB0aGUgbmVlZGVkIGFubm90YXRpb24gKENOViByZXF1aXJlcyBTYW1wbGVJRCBhbmQgb25seSAxIGFubm90YXRpb24pDQpyb3duYW1lcyhhbm5vdGF0aW9uKSA8LSBOVUxMDQpjb2xuYW1lcyhhbm5vdGF0aW9uKSA8LSBOVUxMICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW5mZXJDTlYgcmVxdWlyZXMgbm8gY29sdW1uIGFuZCByb3cgbmFtZXMNCndyaXRlLnRhYmxlKGFubm90YXRpb24sICJBbm5vdGF0aW9ucy50c3YiLCANCiAgICAgICAgICAgIHNlcCA9ICJcdCIsDQogICAgICAgICAgICBxdW90ZSA9IEZBTFNFLCANCiAgICAgICAgICAgIGNvbC5uYW1lcyA9IEZBTFNFLCANCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBNYWtlIGFubm90YXRpb24gZmlsZQ0KYGBgDQoNCg0KYGBge3IgcnVuIENOVl8sIGluY2x1ZGU9RkFMU0V9DQojIFBpY2sgbm9ybWFsL3JlZmVyZW5jZSBncm91cChzKQ0KcmVmZXJlbmNlIDwtIGMoIkNvcnRpY2FsIGdsb21lcnVsdXMiKQ0KDQojIE5hbWUgb3V0cHV0IGZvbGRlcg0Kb3V0X2RpciA8LSAiQ05WIg0KDQojIENyZWF0ZSB0aGUgaW5mZXJjbnYgb2JqZWN0DQppbmZlcmNudl9vYmogPSBDcmVhdGVJbmZlcmNudk9iamVjdChyYXdfY291bnRzX21hdHJpeD0gIkNvdW50cy50c3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbnNfZmlsZT0gIkFubm90YXRpb25zLnRzdiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWxpbT0iXHQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9vcmRlcl9maWxlPSAiZ2VuZV9vcmRlcl9Ic19XVEFfdjFfcGtjLnR4dCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjZ2VuZV9vcmRlcl9maWxlPSAiZ2VuY29kZV92MTlfZ2VuZV9wb3MudHh0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZl9ncm91cF9uYW1lcz0gcmVmZXJlbmNlLCAjIGlucHV0IHRoZSBub3JtYWwvcmVmZXJlbmNlIGdyb3VwIG5hbWVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaHJfZXhjbHVkZSA9IGMoImNock0iKSkgIyBEZWZhdWx0IGV4Y2x1ZGVzIGNoclgsIGNoclkgYW5kIGNock0uIEJ5IG9ubHkgcGlja2luZyBjaHJNIHlvdSBpbmNsdWRlIHRoZSBYIGFuZCBZIGNocm9tb3NvbWVzLiAgICAgDQoNCiMgcGVyZm9ybSBpbmZlcmNudiBvcGVyYXRpb25zIHRvIHJldmVhbCBjbnYgc2lnbmFsLiBGb3IgYWxsIG9wdGlvbnM6IGh0dHBzOi8vcmRyci5pby9naXRodWIvYnJvYWRpbnN0aXR1dGUvaW5mZXJjbnYvbWFuL3J1bi5odG1sDQppbmZlcmNudl9vYmogPSBpbmZlcmNudjo6cnVuKGluZmVyY252X29iaiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3V0b2ZmPTAuMSwgICMgdXNlIDEgZm9yIHNtYXJ0LXNlcSwgMC4xIGZvciAxMHgtZ2Vub21pY3MNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0X2Rpcj0gb3V0X2RpciwgICMgZGlyIGlzIGF1dG8tY3JlYXRlZCBmb3Igc3RvcmluZyBvdXRwdXRzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfYnlfZ3JvdXBzPUYsICAgIyBjbHVzdGVyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbm9pc2U9VCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSE1NPUYsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuYWx5c2lzX21vZGUgPSAic3ViY2x1c3RlcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjLGRlYnVnPVRSVUUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KDQogICAgICAgICAgIyAiR2Vub21lQ29vcmRpbmF0ZXMiOiBbDQogICAgICAgICAgIyAgICJjaHIxMzoxMTIzNzg3MzEtMTEyMzkzMDY5IiwgDQogICAgICAgICAgIyAgICJjaHIxMzoxMTIzODczOTYtMTEyMzkzMDY5IiwgDQogICAgICAgICAgIyAgICJjaHIxMzoxMTIzNzY0NzktMTEyMzkzMDY5Ig0KYGBgDQoNCmBgYHtyIHNob3cgQ05WXywgZmlnLmhlaWdodD0zMCwgZmlnLndpZHRoPTI1fQ0KaW1nIDwtIHJlYWRQTkcocGFzdGUob3V0X2RpciwgIi9pbmZlcmNudi5wbmciLCBzZXAgPSAiIikpDQpncmlkOjpncmlkLm5ld3BhZ2UoKQ0KZ3JpZDo6Z3JpZC5yYXN0ZXIoaW1nKQ0KYGBgDQoNCg==